diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/renderers/webgl/systems/StateSystem.js b/src/core/renderers/webgl/systems/StateSystem.js new file mode 100755 index 0000000..be6c69b --- /dev/null +++ b/src/core/renderers/webgl/systems/StateSystem.js @@ -0,0 +1,289 @@ +import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; +import WebGLSystem from './WebGLSystem'; +import WebGLState from '../State'; + +const BLEND = 0; +const OFFSET = 1; +const CULLING = 2; +const DEPTH_TEST = 3; +const WINDING = 4; + +/** + * A WebGL state machines + * + * @memberof PIXI + * @class + */ +export default class StateSystem extends WebGLSystem +{ + /** + * @param {WebGLRenderingContext} gl - The current WebGL rendering context + */ + constructor(renderer) + { + super(renderer); + + this.gl = null; + + this.maxAttribs = null; + + // check we have vao.. + this.nativeVaoExtension = null; + + this.attribState = null; + + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = 17; + + this.map = []; + + // map functions for when we set state.. + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + + this.checks = []; + + this.defaultState = new WebGLState(); + this.defaultState.blend = true; + this.defaultState.depth = true; + + + } + + contextChange(gl) + { + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = gl; + + this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + // check we have vao.. + this.nativeVaoExtension = ( + gl.getExtension('OES_vertex_array_object') + || gl.getExtension('MOZ_OES_vertex_array_object') + || gl.getExtension('WEBKIT_OES_vertex_array_object') + ); + + this.attribState = { + tempAttribState: new Array(this.maxAttribs), + attribState: new Array(this.maxAttribs), + }; + + this.blendModes = mapWebGLBlendModesToPixi(gl); + + this.setState(this.defaultState); + + this.reset(); + } + + /** + * Sets the current state + * + * @param {*} state - The state to set. + */ + setState(state) + { + state = state || this.defaultState; + + // TODO maybe to an object check? ( this.state === state )? + if (this.stateId !== state.data) + { + let diff = this.stateId ^ state.data; + let i = 0; + + // order from least to most common + while (diff) + { + if (diff & 1) + { + // state change! + this.map[i].call(this, !!(state.data & (1 << i))); + } + + diff = diff >> 1; + i++; + } + + this.stateId = state.data; + } + + // based on the above settings we check for specific modes.. + // for example if blend is active we check and set the blend modes + // or of polygon offset is active we check the poly depth. + for (let i = 0; i < this.checks.length; i++) + { + this.checks[i](this, state); + } + } + + /** + * Enables or disabled blending. + * + * @param {boolean} value - Turn on or off webgl blending. + */ + setBlend(value) + { + this.updateCheck(StateSystem.checkBlendMode, value); + + this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); + } + + /** + * Enables or disable polygon offset fill + * + * @param {boolean} value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); + } + + /** + * Sets whether to enable or disable depth test. + * + * @param {boolean} value - Turn on or off webgl depth testing. + */ + setDepthTest(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); + } + + /** + * Sets whether to enable or disable cull face. + * + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); + } + + /** + * Sets the gl front face. + * + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) + { + this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); + } + + /** + * Sets the blend mode. + * + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) + { + if (value === this.blendMode) + { + return; + } + + this.blendMode = value; + this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + } + + /** + * Sets the polygon offset. + * + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) + { + this.gl.polygonOffset(value, scale); + } + + /** + * Disables all the vaos in use + * + */ + resetAttributes() + { + for (let i = 0; i < this.attribState.tempAttribState.length; i++) + { + this.attribState.tempAttribState[i] = 0; + } + + for (let i = 0; i < this.attribState.attribState.length; i++) + { + this.attribState.attribState[i] = 0; + } + + // im going to assume one is always active for performance reasons. + for (let i = 1; i < this.maxAttribs; i++) + { + this.gl.disableVertexAttribArray(i); + } + } + + // used + /** + * Resets all the logic and disables the vaos + */ + reset() + { + // unbind any VAO if they exist.. + if (this.nativeVaoExtension) + { + this.nativeVaoExtension.bindVertexArrayOES(null); + } + + // reset all attributes.. + this.resetAttributes(); + + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + + this.setBlendMode(0); + + // TODO? + // this.setState(this.defaultState); + } + + /** + * checks to see which updates should be checked based on which settings have been activated + * for example if blend is enabled then we shold check the blend modes each time the state is changed + * or if poygon fill is activated then we need to check if the polygone offset changes. + * The idea is that we only check what we have too. + * + * @param {Function} func the checking function to add or remove + * @param {boolean} value should the check function be added or removed. + */ + updateCheck(func, value) + { + const index = this.checks.indexOf(func); + + if (value && index === -1) + { + this.checks.push(func); + } + else if (!value && index !== -1) + { + this.checks.splice(index, 1); + } + } + + /** + * A private little wrapper function that we call to check the blend mode. + * + * @static + * @private + * @param {PIXI.StateSystem} System the System to perform the state check on + * @param {PIXI.State} state the state that the blendMode will pulled from + */ + static checkBlendMode(system, state) + { + system.setBlendMode(state.blendMode); + } + + // TODO - add polygon offset? +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/renderers/webgl/systems/StateSystem.js b/src/core/renderers/webgl/systems/StateSystem.js new file mode 100755 index 0000000..be6c69b --- /dev/null +++ b/src/core/renderers/webgl/systems/StateSystem.js @@ -0,0 +1,289 @@ +import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; +import WebGLSystem from './WebGLSystem'; +import WebGLState from '../State'; + +const BLEND = 0; +const OFFSET = 1; +const CULLING = 2; +const DEPTH_TEST = 3; +const WINDING = 4; + +/** + * A WebGL state machines + * + * @memberof PIXI + * @class + */ +export default class StateSystem extends WebGLSystem +{ + /** + * @param {WebGLRenderingContext} gl - The current WebGL rendering context + */ + constructor(renderer) + { + super(renderer); + + this.gl = null; + + this.maxAttribs = null; + + // check we have vao.. + this.nativeVaoExtension = null; + + this.attribState = null; + + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = 17; + + this.map = []; + + // map functions for when we set state.. + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + + this.checks = []; + + this.defaultState = new WebGLState(); + this.defaultState.blend = true; + this.defaultState.depth = true; + + + } + + contextChange(gl) + { + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = gl; + + this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + // check we have vao.. + this.nativeVaoExtension = ( + gl.getExtension('OES_vertex_array_object') + || gl.getExtension('MOZ_OES_vertex_array_object') + || gl.getExtension('WEBKIT_OES_vertex_array_object') + ); + + this.attribState = { + tempAttribState: new Array(this.maxAttribs), + attribState: new Array(this.maxAttribs), + }; + + this.blendModes = mapWebGLBlendModesToPixi(gl); + + this.setState(this.defaultState); + + this.reset(); + } + + /** + * Sets the current state + * + * @param {*} state - The state to set. + */ + setState(state) + { + state = state || this.defaultState; + + // TODO maybe to an object check? ( this.state === state )? + if (this.stateId !== state.data) + { + let diff = this.stateId ^ state.data; + let i = 0; + + // order from least to most common + while (diff) + { + if (diff & 1) + { + // state change! + this.map[i].call(this, !!(state.data & (1 << i))); + } + + diff = diff >> 1; + i++; + } + + this.stateId = state.data; + } + + // based on the above settings we check for specific modes.. + // for example if blend is active we check and set the blend modes + // or of polygon offset is active we check the poly depth. + for (let i = 0; i < this.checks.length; i++) + { + this.checks[i](this, state); + } + } + + /** + * Enables or disabled blending. + * + * @param {boolean} value - Turn on or off webgl blending. + */ + setBlend(value) + { + this.updateCheck(StateSystem.checkBlendMode, value); + + this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); + } + + /** + * Enables or disable polygon offset fill + * + * @param {boolean} value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); + } + + /** + * Sets whether to enable or disable depth test. + * + * @param {boolean} value - Turn on or off webgl depth testing. + */ + setDepthTest(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); + } + + /** + * Sets whether to enable or disable cull face. + * + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); + } + + /** + * Sets the gl front face. + * + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) + { + this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); + } + + /** + * Sets the blend mode. + * + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) + { + if (value === this.blendMode) + { + return; + } + + this.blendMode = value; + this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + } + + /** + * Sets the polygon offset. + * + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) + { + this.gl.polygonOffset(value, scale); + } + + /** + * Disables all the vaos in use + * + */ + resetAttributes() + { + for (let i = 0; i < this.attribState.tempAttribState.length; i++) + { + this.attribState.tempAttribState[i] = 0; + } + + for (let i = 0; i < this.attribState.attribState.length; i++) + { + this.attribState.attribState[i] = 0; + } + + // im going to assume one is always active for performance reasons. + for (let i = 1; i < this.maxAttribs; i++) + { + this.gl.disableVertexAttribArray(i); + } + } + + // used + /** + * Resets all the logic and disables the vaos + */ + reset() + { + // unbind any VAO if they exist.. + if (this.nativeVaoExtension) + { + this.nativeVaoExtension.bindVertexArrayOES(null); + } + + // reset all attributes.. + this.resetAttributes(); + + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + + this.setBlendMode(0); + + // TODO? + // this.setState(this.defaultState); + } + + /** + * checks to see which updates should be checked based on which settings have been activated + * for example if blend is enabled then we shold check the blend modes each time the state is changed + * or if poygon fill is activated then we need to check if the polygone offset changes. + * The idea is that we only check what we have too. + * + * @param {Function} func the checking function to add or remove + * @param {boolean} value should the check function be added or removed. + */ + updateCheck(func, value) + { + const index = this.checks.indexOf(func); + + if (value && index === -1) + { + this.checks.push(func); + } + else if (!value && index !== -1) + { + this.checks.splice(index, 1); + } + } + + /** + * A private little wrapper function that we call to check the blend mode. + * + * @static + * @private + * @param {PIXI.StateSystem} System the System to perform the state check on + * @param {PIXI.State} state the state that the blendMode will pulled from + */ + static checkBlendMode(system, state) + { + system.setBlendMode(state.blendMode); + } + + // TODO - add polygon offset? +} diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js new file mode 100644 index 0000000..3ecd92d --- /dev/null +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -0,0 +1,113 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class StencilSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.stencilMaskStack = null; + } + + /** + * Changes the mask stack that is used by this System. + * + * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack + */ + setMaskStack(stencilMaskStack) + { + this.stencilMaskStack = stencilMaskStack; + + const gl = this.renderer.gl; + + if (stencilMaskStack.length === 0) + { + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.enable(gl.STENCIL_TEST); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. @alvin + * + * @param {PIXI.Graphics} graphics - The mask + */ + pushStencil(graphics) + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + this.renderer._activeRenderTarget.attachStencilBuffer(); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + if (sms.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(gl.ALWAYS, 1, 1); + } + + sms.push(graphics); + + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + + /** + * TODO @alvin + */ + popStencil() + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + const graphics = sms.pop(); + + if (sms.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + } + + /** + * Destroys the mask stack. + * + */ + destroy() + { + super.destroy(this); + + this.stencilMaskStack.stencilStack = null; + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/renderers/webgl/systems/StateSystem.js b/src/core/renderers/webgl/systems/StateSystem.js new file mode 100755 index 0000000..be6c69b --- /dev/null +++ b/src/core/renderers/webgl/systems/StateSystem.js @@ -0,0 +1,289 @@ +import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; +import WebGLSystem from './WebGLSystem'; +import WebGLState from '../State'; + +const BLEND = 0; +const OFFSET = 1; +const CULLING = 2; +const DEPTH_TEST = 3; +const WINDING = 4; + +/** + * A WebGL state machines + * + * @memberof PIXI + * @class + */ +export default class StateSystem extends WebGLSystem +{ + /** + * @param {WebGLRenderingContext} gl - The current WebGL rendering context + */ + constructor(renderer) + { + super(renderer); + + this.gl = null; + + this.maxAttribs = null; + + // check we have vao.. + this.nativeVaoExtension = null; + + this.attribState = null; + + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = 17; + + this.map = []; + + // map functions for when we set state.. + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + + this.checks = []; + + this.defaultState = new WebGLState(); + this.defaultState.blend = true; + this.defaultState.depth = true; + + + } + + contextChange(gl) + { + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = gl; + + this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + // check we have vao.. + this.nativeVaoExtension = ( + gl.getExtension('OES_vertex_array_object') + || gl.getExtension('MOZ_OES_vertex_array_object') + || gl.getExtension('WEBKIT_OES_vertex_array_object') + ); + + this.attribState = { + tempAttribState: new Array(this.maxAttribs), + attribState: new Array(this.maxAttribs), + }; + + this.blendModes = mapWebGLBlendModesToPixi(gl); + + this.setState(this.defaultState); + + this.reset(); + } + + /** + * Sets the current state + * + * @param {*} state - The state to set. + */ + setState(state) + { + state = state || this.defaultState; + + // TODO maybe to an object check? ( this.state === state )? + if (this.stateId !== state.data) + { + let diff = this.stateId ^ state.data; + let i = 0; + + // order from least to most common + while (diff) + { + if (diff & 1) + { + // state change! + this.map[i].call(this, !!(state.data & (1 << i))); + } + + diff = diff >> 1; + i++; + } + + this.stateId = state.data; + } + + // based on the above settings we check for specific modes.. + // for example if blend is active we check and set the blend modes + // or of polygon offset is active we check the poly depth. + for (let i = 0; i < this.checks.length; i++) + { + this.checks[i](this, state); + } + } + + /** + * Enables or disabled blending. + * + * @param {boolean} value - Turn on or off webgl blending. + */ + setBlend(value) + { + this.updateCheck(StateSystem.checkBlendMode, value); + + this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); + } + + /** + * Enables or disable polygon offset fill + * + * @param {boolean} value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); + } + + /** + * Sets whether to enable or disable depth test. + * + * @param {boolean} value - Turn on or off webgl depth testing. + */ + setDepthTest(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); + } + + /** + * Sets whether to enable or disable cull face. + * + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); + } + + /** + * Sets the gl front face. + * + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) + { + this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); + } + + /** + * Sets the blend mode. + * + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) + { + if (value === this.blendMode) + { + return; + } + + this.blendMode = value; + this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + } + + /** + * Sets the polygon offset. + * + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) + { + this.gl.polygonOffset(value, scale); + } + + /** + * Disables all the vaos in use + * + */ + resetAttributes() + { + for (let i = 0; i < this.attribState.tempAttribState.length; i++) + { + this.attribState.tempAttribState[i] = 0; + } + + for (let i = 0; i < this.attribState.attribState.length; i++) + { + this.attribState.attribState[i] = 0; + } + + // im going to assume one is always active for performance reasons. + for (let i = 1; i < this.maxAttribs; i++) + { + this.gl.disableVertexAttribArray(i); + } + } + + // used + /** + * Resets all the logic and disables the vaos + */ + reset() + { + // unbind any VAO if they exist.. + if (this.nativeVaoExtension) + { + this.nativeVaoExtension.bindVertexArrayOES(null); + } + + // reset all attributes.. + this.resetAttributes(); + + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + + this.setBlendMode(0); + + // TODO? + // this.setState(this.defaultState); + } + + /** + * checks to see which updates should be checked based on which settings have been activated + * for example if blend is enabled then we shold check the blend modes each time the state is changed + * or if poygon fill is activated then we need to check if the polygone offset changes. + * The idea is that we only check what we have too. + * + * @param {Function} func the checking function to add or remove + * @param {boolean} value should the check function be added or removed. + */ + updateCheck(func, value) + { + const index = this.checks.indexOf(func); + + if (value && index === -1) + { + this.checks.push(func); + } + else if (!value && index !== -1) + { + this.checks.splice(index, 1); + } + } + + /** + * A private little wrapper function that we call to check the blend mode. + * + * @static + * @private + * @param {PIXI.StateSystem} System the System to perform the state check on + * @param {PIXI.State} state the state that the blendMode will pulled from + */ + static checkBlendMode(system, state) + { + system.setBlendMode(state.blendMode); + } + + // TODO - add polygon offset? +} diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js new file mode 100644 index 0000000..3ecd92d --- /dev/null +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -0,0 +1,113 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class StencilSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.stencilMaskStack = null; + } + + /** + * Changes the mask stack that is used by this System. + * + * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack + */ + setMaskStack(stencilMaskStack) + { + this.stencilMaskStack = stencilMaskStack; + + const gl = this.renderer.gl; + + if (stencilMaskStack.length === 0) + { + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.enable(gl.STENCIL_TEST); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. @alvin + * + * @param {PIXI.Graphics} graphics - The mask + */ + pushStencil(graphics) + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + this.renderer._activeRenderTarget.attachStencilBuffer(); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + if (sms.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(gl.ALWAYS, 1, 1); + } + + sms.push(graphics); + + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + + /** + * TODO @alvin + */ + popStencil() + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + const graphics = sms.pop(); + + if (sms.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + } + + /** + * Destroys the mask stack. + * + */ + destroy() + { + super.destroy(this); + + this.stencilMaskStack.stencilStack = null; + } +} diff --git a/src/core/renderers/webgl/systems/TextureGCSystem.js b/src/core/renderers/webgl/systems/TextureGCSystem.js new file mode 100644 index 0000000..cd9f24b --- /dev/null +++ b/src/core/renderers/webgl/systems/TextureGCSystem.js @@ -0,0 +1,111 @@ +import WebGLSystem from './WebGLSystem'; +import { GC_MODES } from '../../../const'; +import settings from '../../../settings'; + +/** + * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged + * up with textures that are no longer being used. + * + * @class + * @memberof PIXI + */ +export default class TextureGCSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.count = 0; + this.checkCount = 0; + this.maxIdle = settings.GC_MAX_IDLE; + this.checkCountMax = settings.GC_MAX_CHECK_COUNT; + this.mode = settings.GC_MODE; + } + + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + postrender() + { + this.count++; + + if (this.mode === GC_MODES.MANUAL) + { + return; + } + + this.checkCount++; + + if (this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } + } + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + run() + { + const tm = this.renderer.texture; + const managedTextures = tm._managedTextures; + let wasRemoved = false; + + for (let i = 0; i < managedTextures.length; i++) + { + const texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + let j = 0; + + for (let i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } + } + + /** + * Removes all the textures within the specified displayObject and its children from the GPU + * + * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. + */ + unload(displayObject) + { + const tm = this.renderer.textureSystem; + + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) + { + tm.destroyTexture(displayObject._texture, true); + } + + for (let i = displayObject.children.length - 1; i >= 0; i--) + { + this.unload(displayObject.children[i]); + } + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/renderers/webgl/systems/StateSystem.js b/src/core/renderers/webgl/systems/StateSystem.js new file mode 100755 index 0000000..be6c69b --- /dev/null +++ b/src/core/renderers/webgl/systems/StateSystem.js @@ -0,0 +1,289 @@ +import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; +import WebGLSystem from './WebGLSystem'; +import WebGLState from '../State'; + +const BLEND = 0; +const OFFSET = 1; +const CULLING = 2; +const DEPTH_TEST = 3; +const WINDING = 4; + +/** + * A WebGL state machines + * + * @memberof PIXI + * @class + */ +export default class StateSystem extends WebGLSystem +{ + /** + * @param {WebGLRenderingContext} gl - The current WebGL rendering context + */ + constructor(renderer) + { + super(renderer); + + this.gl = null; + + this.maxAttribs = null; + + // check we have vao.. + this.nativeVaoExtension = null; + + this.attribState = null; + + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = 17; + + this.map = []; + + // map functions for when we set state.. + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + + this.checks = []; + + this.defaultState = new WebGLState(); + this.defaultState.blend = true; + this.defaultState.depth = true; + + + } + + contextChange(gl) + { + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = gl; + + this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + // check we have vao.. + this.nativeVaoExtension = ( + gl.getExtension('OES_vertex_array_object') + || gl.getExtension('MOZ_OES_vertex_array_object') + || gl.getExtension('WEBKIT_OES_vertex_array_object') + ); + + this.attribState = { + tempAttribState: new Array(this.maxAttribs), + attribState: new Array(this.maxAttribs), + }; + + this.blendModes = mapWebGLBlendModesToPixi(gl); + + this.setState(this.defaultState); + + this.reset(); + } + + /** + * Sets the current state + * + * @param {*} state - The state to set. + */ + setState(state) + { + state = state || this.defaultState; + + // TODO maybe to an object check? ( this.state === state )? + if (this.stateId !== state.data) + { + let diff = this.stateId ^ state.data; + let i = 0; + + // order from least to most common + while (diff) + { + if (diff & 1) + { + // state change! + this.map[i].call(this, !!(state.data & (1 << i))); + } + + diff = diff >> 1; + i++; + } + + this.stateId = state.data; + } + + // based on the above settings we check for specific modes.. + // for example if blend is active we check and set the blend modes + // or of polygon offset is active we check the poly depth. + for (let i = 0; i < this.checks.length; i++) + { + this.checks[i](this, state); + } + } + + /** + * Enables or disabled blending. + * + * @param {boolean} value - Turn on or off webgl blending. + */ + setBlend(value) + { + this.updateCheck(StateSystem.checkBlendMode, value); + + this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); + } + + /** + * Enables or disable polygon offset fill + * + * @param {boolean} value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); + } + + /** + * Sets whether to enable or disable depth test. + * + * @param {boolean} value - Turn on or off webgl depth testing. + */ + setDepthTest(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); + } + + /** + * Sets whether to enable or disable cull face. + * + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); + } + + /** + * Sets the gl front face. + * + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) + { + this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); + } + + /** + * Sets the blend mode. + * + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) + { + if (value === this.blendMode) + { + return; + } + + this.blendMode = value; + this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + } + + /** + * Sets the polygon offset. + * + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) + { + this.gl.polygonOffset(value, scale); + } + + /** + * Disables all the vaos in use + * + */ + resetAttributes() + { + for (let i = 0; i < this.attribState.tempAttribState.length; i++) + { + this.attribState.tempAttribState[i] = 0; + } + + for (let i = 0; i < this.attribState.attribState.length; i++) + { + this.attribState.attribState[i] = 0; + } + + // im going to assume one is always active for performance reasons. + for (let i = 1; i < this.maxAttribs; i++) + { + this.gl.disableVertexAttribArray(i); + } + } + + // used + /** + * Resets all the logic and disables the vaos + */ + reset() + { + // unbind any VAO if they exist.. + if (this.nativeVaoExtension) + { + this.nativeVaoExtension.bindVertexArrayOES(null); + } + + // reset all attributes.. + this.resetAttributes(); + + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + + this.setBlendMode(0); + + // TODO? + // this.setState(this.defaultState); + } + + /** + * checks to see which updates should be checked based on which settings have been activated + * for example if blend is enabled then we shold check the blend modes each time the state is changed + * or if poygon fill is activated then we need to check if the polygone offset changes. + * The idea is that we only check what we have too. + * + * @param {Function} func the checking function to add or remove + * @param {boolean} value should the check function be added or removed. + */ + updateCheck(func, value) + { + const index = this.checks.indexOf(func); + + if (value && index === -1) + { + this.checks.push(func); + } + else if (!value && index !== -1) + { + this.checks.splice(index, 1); + } + } + + /** + * A private little wrapper function that we call to check the blend mode. + * + * @static + * @private + * @param {PIXI.StateSystem} System the System to perform the state check on + * @param {PIXI.State} state the state that the blendMode will pulled from + */ + static checkBlendMode(system, state) + { + system.setBlendMode(state.blendMode); + } + + // TODO - add polygon offset? +} diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js new file mode 100644 index 0000000..3ecd92d --- /dev/null +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -0,0 +1,113 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class StencilSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.stencilMaskStack = null; + } + + /** + * Changes the mask stack that is used by this System. + * + * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack + */ + setMaskStack(stencilMaskStack) + { + this.stencilMaskStack = stencilMaskStack; + + const gl = this.renderer.gl; + + if (stencilMaskStack.length === 0) + { + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.enable(gl.STENCIL_TEST); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. @alvin + * + * @param {PIXI.Graphics} graphics - The mask + */ + pushStencil(graphics) + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + this.renderer._activeRenderTarget.attachStencilBuffer(); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + if (sms.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(gl.ALWAYS, 1, 1); + } + + sms.push(graphics); + + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + + /** + * TODO @alvin + */ + popStencil() + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + const graphics = sms.pop(); + + if (sms.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + } + + /** + * Destroys the mask stack. + * + */ + destroy() + { + super.destroy(this); + + this.stencilMaskStack.stencilStack = null; + } +} diff --git a/src/core/renderers/webgl/systems/TextureGCSystem.js b/src/core/renderers/webgl/systems/TextureGCSystem.js new file mode 100644 index 0000000..cd9f24b --- /dev/null +++ b/src/core/renderers/webgl/systems/TextureGCSystem.js @@ -0,0 +1,111 @@ +import WebGLSystem from './WebGLSystem'; +import { GC_MODES } from '../../../const'; +import settings from '../../../settings'; + +/** + * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged + * up with textures that are no longer being used. + * + * @class + * @memberof PIXI + */ +export default class TextureGCSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.count = 0; + this.checkCount = 0; + this.maxIdle = settings.GC_MAX_IDLE; + this.checkCountMax = settings.GC_MAX_CHECK_COUNT; + this.mode = settings.GC_MODE; + } + + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + postrender() + { + this.count++; + + if (this.mode === GC_MODES.MANUAL) + { + return; + } + + this.checkCount++; + + if (this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } + } + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + run() + { + const tm = this.renderer.texture; + const managedTextures = tm._managedTextures; + let wasRemoved = false; + + for (let i = 0; i < managedTextures.length; i++) + { + const texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + let j = 0; + + for (let i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } + } + + /** + * Removes all the textures within the specified displayObject and its children from the GPU + * + * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. + */ + unload(displayObject) + { + const tm = this.renderer.textureSystem; + + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) + { + tm.destroyTexture(displayObject._texture, true); + } + + for (let i = displayObject.children.length - 1; i >= 0; i--) + { + this.unload(displayObject.children[i]); + } + } +} diff --git a/src/core/renderers/webgl/systems/WebGLSystem.js b/src/core/renderers/webgl/systems/WebGLSystem.js new file mode 100644 index 0000000..1fe53a7 --- /dev/null +++ b/src/core/renderers/webgl/systems/WebGLSystem.js @@ -0,0 +1,40 @@ +/** + * @class + * @memberof PIXI + */ +export default class WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + /** + * The renderer this manager works for. + * + * @member {PIXI.WebGLRenderer} + */ + this.renderer = renderer; + + this.renderer.runners.contextChange.add(this); + } + + /** + * Generic method called when there is a WebGL context change. + * + */ + contextChange() + { + // do some codes init! + } + + /** + * Generic destroy methods to be overridden by the subclass + * + */ + destroy() + { + this.renderer.runners.contextChange.remove(this); + this.renderer = null; + } +} diff --git a/src/core/index.js b/src/core/index.js index 4cb6778..76eaa44 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -41,7 +41,7 @@ export { default as VideoBaseTexture } from './textures/VideoBaseTexture'; export { default as TextureUvs } from './textures/TextureUvs'; export { default as CanvasRenderTarget } from './renderers/canvas/utils/CanvasRenderTarget'; -export { default as WebGLManager } from './renderers/webgl/managers/WebGLManager'; +export { default as WebGLSystem } from './renderers/webgl/systems/WebGLSystem'; export { default as State } from './renderers/webgl/State'; export { default as ObjectRenderer } from './renderers/webgl/utils/ObjectRenderer'; export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 3c4869a..712e179 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,18 +1,18 @@ import SystemRenderer from '../SystemRenderer'; -import MaskManager from './managers/MaskManager'; -import StencilManager from './managers/StencilManager'; -import FilterManager from './managers/FilterManager'; -import FramebufferManager from './managers/FramebufferManager'; -import RenderTextureManager from './managers/RenderTextureManager'; -import NewTextureManager from './managers/NewTextureManager'; -import TextureManager from './TextureManager'; -import ProjectionManager from './managers/ProjectionManager'; -import StateManager from './managers/StateManager'; -import GeometryManager from './managers/GeometryManager'; -import ShaderManager from './managers/ShaderManager'; -import ContextManager from './managers/ContextManager'; -import BatchManager from './managers/BatchManager'; -import TextureGCManager from './managers/TextureGCManager'; +import MaskSystem from './systems/MaskSystem'; +import StencilSystem from './systems/StencilSystem'; +import FilterSystem from './systems/FilterSystem'; +import FramebufferSystem from './systems/FramebufferSystem'; +import RenderTextureSystem from './systems/RenderTextureSystem'; +import NewTextureSystem from './systems/NewTextureSystem'; +import TextureSystem from './TextureManager'; +import ProjectionSystem from './systems/ProjectionSystem'; +import StateSystem from './systems/StateSystem'; +import GeometrySystem from './systems/GeometrySystem'; +import ShaderSystem from './systems/ShaderSystem'; +import ContextSystem from './systems/ContextSystem'; +import BatchSystem from './systems/BatchSystem'; +import TextureGCSystem from './systems/TextureGCSystem'; import { pluginTarget } from '../../utils'; import glCore from 'pixi-gl-core'; import { RENDERER_TYPE } from '../../const'; @@ -68,7 +68,7 @@ */ this.type = RENDERER_TYPE.WEBGL; - // this will be set by the contextManager (this.context) + // this will be set by the contextSystem (this.context) this.gl = null; this.CONTEXT_UID = 0; this.legacy = !!options.legacy; @@ -102,19 +102,19 @@ projectionMatrix:new Matrix() }, true) - this.addManager(MaskManager) - .addManager(ContextManager) - .addManager(StateManager) - .addManager(ShaderManager) - .addManager(NewTextureManager, 'texture') - .addManager(GeometryManager) - .addManager(FramebufferManager) - .addManager(StencilManager) - .addManager(ProjectionManager) - .addManager(TextureGCManager) - .addManager(FilterManager) - .addManager(RenderTextureManager) - .addManager(BatchManager) + this.addSystem(MaskSystem) + .addSystem(ContextSystem) + .addSystem(StateSystem) + .addSystem(ShaderSystem) + .addSystem(NewTextureSystem, 'texture') + .addSystem(GeometrySystem) + .addSystem(FramebufferSystem) + .addSystem(StencilSystem) + .addSystem(ProjectionSystem) + .addSystem(TextureGCSystem) + .addSystem(FilterSystem) + .addSystem(RenderTextureSystem) + .addSystem(BatchSystem) this.initPlugins(); @@ -140,7 +140,7 @@ this._initContext(); } - addManager(_class, name) + addSystem(_class, name) { if(!name) { @@ -148,13 +148,13 @@ } //TODO - read name from class.name.. - if(name.includes('Manager')) + if(name.includes('System')) { - name = name.replace('Manager', ''); + name = name.replace('System', ''); name = name.charAt(0).toLowerCase() + name.slice(1); } - const manager = new _class(this); + const system = new _class(this); if(this[name]) { @@ -162,11 +162,11 @@ return; } - this[name] = manager; + this[name] = system; for(var i in this.runners) { - this.runners[i].add(manager); + this.runners[i].add(system); } return this; diff --git a/src/core/renderers/webgl/managers/BatchManager.js b/src/core/renderers/webgl/managers/BatchManager.js deleted file mode 100644 index ebda417..0000000 --- a/src/core/renderers/webgl/managers/BatchManager.js +++ /dev/null @@ -1,69 +0,0 @@ -import WebGLManager from './WebGLManager'; -import ObjectRenderer from '../utils/ObjectRenderer'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class BatchManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - /** - * An empty renderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.emptyRenderer = new ObjectRenderer(renderer); - - /** - * The currently active ObjectRenderer. - * - * @member {PIXI.ObjectRenderer} - */ - this.currentRenderer = this.emptyRenderer; - } - - /** - * Changes the current renderer to the one given in parameter - * - * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. - */ - setObjectRenderer(objectRenderer) - { - if (this.currentRenderer === objectRenderer) - { - return; - } - - this.currentRenderer.stop(); - this.currentRenderer = objectRenderer; - - this.renderer.state.setState(objectRenderer.state); - - this.currentRenderer.start(); - } - - /** - * This should be called if you wish to do some custom rendering - * It will basically render anything that may be batched up such as sprites - * - */ - flush() - { - this.setObjectRenderer(this.emptyRenderer); - } - - reset() - { - this.setObjectRenderer(this.emptyRenderer); - } -} diff --git a/src/core/renderers/webgl/managers/ContextManager.js b/src/core/renderers/webgl/managers/ContextManager.js deleted file mode 100644 index ccde2b1..0000000 --- a/src/core/renderers/webgl/managers/ContextManager.js +++ /dev/null @@ -1,119 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; -import validateContext from '../utils/validateContext'; - -let CONTEXT_UID = 0; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ContextManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.handleContextLost = this.handleContextLost.bind(this); - this.handleContextRestored = this.handleContextRestored.bind(this); - - renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); - renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); - - } - - get isLost() - { - return (!this.gl || this.gl.isContextLost()); - } - - contextChange(gl) - { - this.gl = gl; - - // restore a context if it was previously lost - if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) - { - gl.getExtension('WEBGL_lose_context').restoreContext(); - } - - // set the latest testing context.. - if(!glCore._testingContext) - { - glCore._testingContext = gl; - } - - // setup the width/height properties and gl viewport - //this.resize(this.screen.width, this.screen.height); - // const renderer = this.renderer; - - // renderer.resize(renderer.screen.width, renderer.screen.height); - } - - initFromContext(gl) - { - this.gl = gl; - validateContext(gl); - this.renderer.gl = gl; - this.renderer.CONTEXT_UID = CONTEXT_UID++; - this.renderer.runners.contextChange.run(gl); - } - - initFromOptions(options) - { - const gl = glCore.createContext(this.renderer.view, this.options); - this.initFromContext(gl); - } - - /** - * Handles a lost webgl context - * - * @private - * @param {WebGLContextEvent} event - The context lost event. - */ - handleContextLost(event) - { - event.preventDefault(); - } - - /** - * Handles a restored webgl context - * - * @private - */ - handleContextRestored() - { - this.renderer.runners.contextChange.run(gl); - - // TODO - tidy up textures? - //this.textureManager.removeAll(); - } - - destroy() - { - const view = this.renderer.view; - - // remove listeners - view.removeEventListener('webglcontextlost', this.handleContextLost); - view.removeEventListener('webglcontextrestored', this.handleContextRestored); - - this.gl.useProgram(null); - - if (this.gl.getExtension('WEBGL_lose_context')) - { - this.gl.getExtension('WEBGL_lose_context').loseContext(); - } - - } - - postrender() - { - this.gl.flush(); - } -} diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js deleted file mode 100644 index 09104d7..0000000 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ /dev/null @@ -1,588 +0,0 @@ -import WebGLManager from './WebGLManager'; -import RenderTarget from '../utils/RenderTarget'; -import Quad from '../utils/Quad'; -import { Rectangle } from '../../../math'; -import Shader from '../../../Shader'; -import * as filterTransforms from '../filters/filterTransforms'; -import bitTwiddle from 'bit-twiddle'; - -/** - * @ignore - * @class - */ -class FilterState -{ - /** - * - */ - constructor() - { - this.renderTarget = null; - this.sourceFrame = new Rectangle(); - this.destinationFrame = new Rectangle(); - this.filters = []; - this.target = null; - this.resolution = 1; - } -} - -/** - * @class - * @memberof PIXI - * @extends PIXI.WebGLManager - */ -export default class FilterManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - - this.shaderCache = {}; - // todo add default! - this.pool = {}; - - this.filterData = null; - } - - contextChange() - { - this.gl = this.renderer.gl; - // know about sprites! - this.quad = new Quad(this.gl, this.renderer.state.attribState); - - } - - /** - * Adds a new filter to the manager. - * - * @param {PIXI.DisplayObject} target - The target of the filter to render. - * @param {PIXI.Filter[]} filters - The filters to apply. - */ - pushFilter(target, filters) - { - const renderer = this.renderer; - - let filterData = this.filterData; - - if (!filterData) - { - filterData = this.renderer._activeRenderTarget.filterStack; - - // add new stack - const filterState = new FilterState(); - - filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; - filterState.renderTarget = renderer._activeRenderTarget; - - this.renderer._activeRenderTarget.filterData = filterData = { - index: 0, - stack: [filterState], - }; - - this.filterData = filterData; - } - - // get the current filter state.. - let currentState = filterData.stack[++filterData.index]; - - if (!currentState) - { - currentState = filterData.stack[filterData.index] = new FilterState(); - } - - // for now we go off the filter of the first resolution.. - const resolution = filters[0].resolution; - const padding = filters[0].padding | 0; - const targetBounds = target.filterArea || target.getBounds(true); - const sourceFrame = currentState.sourceFrame; - const destinationFrame = currentState.destinationFrame; - - sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; - sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; - sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; - sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; - - if (filterData.stack[0].renderTarget.transform) - { // - - // TODO we should fit the rect around the transform.. - } - else - { - sourceFrame.fit(filterData.stack[0].destinationFrame); - } - - // lets apply the padding After we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - sourceFrame.pad(padding); - - destinationFrame.width = sourceFrame.width; - destinationFrame.height = sourceFrame.height; - - // lets play the padding after we fit the element to the screen. - // this should stop the strange side effects that can occur when cropping to the edges - - const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); - - currentState.target = target; - currentState.filters = filters; - currentState.resolution = resolution; - currentState.renderTarget = renderTarget; - - // bind the render target to draw the shape in the top corner.. - - renderTarget.setFrame(destinationFrame, sourceFrame); - - // bind the render target - renderer.bindRenderTarget(renderTarget); - renderTarget.clear(); - } - - /** - * Pops off the filter and applies it. - * - */ - popFilter() - { - const filterData = this.filterData; - - const lastState = filterData.stack[filterData.index - 1]; - const currentState = filterData.stack[filterData.index]; - - this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); - - const filters = currentState.filters; - - if (filters.length === 1) - { - filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); - this.freePotRenderTarget(currentState.renderTarget); - } - else - { - let flip = currentState.renderTarget; - let flop = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - currentState.resolution - ); - - flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - // finally lets clear the render target before drawing to it.. - flop.clear(); - - let i = 0; - - for (i = 0; i < filters.length - 1; ++i) - { - filters[i].apply(this, flip, flop, true, currentState); - - const t = flip; - - flip = flop; - flop = t; - } - - filters[i].apply(this, flip, lastState.renderTarget, false, currentState); - - this.freePotRenderTarget(flip); - this.freePotRenderTarget(flop); - } - - filterData.index--; - - if (filterData.index === 0) - { - this.filterData = null; - } - } - - /** - * Draws a filter. - * - * @param {PIXI.Filter} filter - The filter to draw. - * @param {PIXI.RenderTarget} input - The input render target. - * @param {PIXI.RenderTarget} output - The target to output to. - * @param {boolean} clear - Should the output be cleared before rendering to it - */ - applyFilter(filter, input, output, clear) - { - const renderer = this.renderer; - const gl = renderer.gl; - - let shader = filter.glShaders[renderer.CONTEXT_UID]; - - // cacheing.. - if (!shader) - { - if (filter.glShaderKey) - { - shader = this.shaderCache[filter.glShaderKey]; - - if (!shader) - { - shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - - filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; - } - } - else - { - shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); - } - - // TODO - this only needs to be done once? - renderer.bindVao(null); - - this.quad.initVao(shader); - } - - renderer.bindVao(this.quad.vao); - - renderer.bindRenderTarget(output); - - if (clear) - { - gl.disable(gl.SCISSOR_TEST); - renderer.clear();// [1, 1, 1, 1]); - gl.enable(gl.SCISSOR_TEST); - } - - // in case the render target is being masked using a scissor rect - if (output === renderer.maskManager.scissorRenderTarget) - { - renderer.maskManager.pushScissorMask(null, renderer.maskManager.scissorData); - } - - renderer.bindShader(shader); - - // free unit 0 for us, doesn't matter what was there - // don't try to restore it, because syncUniforms can upload it to another slot - // and it'll be a problem - const tex = this.renderer.emptyTextures[0]; - - this.renderer.boundTextures[0] = tex; - // this syncs the pixi filters uniforms with glsl uniforms - this.syncUniforms(shader, filter); - - renderer.state.setBlendMode(filter.blendMode); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); - - this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); - - gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); - } - - /** - * Uploads the uniforms of the filter. - * - * @param {GLShader} shader - The underlying gl shader. - * @param {PIXI.Filter} filter - The filter we are synchronizing. - */ - syncUniforms(shader, filter) - { - const uniformData = filter.uniformData; - const uniforms = filter.uniforms; - - // 0 is reserved for the pixi texture so we start at 1! - let textureCount = 1; - let currentState; - - // filterArea and filterClamp that are handled by FilterManager directly - // they must not appear in uniformData - - if (shader.uniforms.filterArea) - { - currentState = this.filterData.stack[this.filterData.index]; - - const filterArea = shader.uniforms.filterArea; - - filterArea[0] = currentState.renderTarget.size.width; - filterArea[1] = currentState.renderTarget.size.height; - filterArea[2] = currentState.sourceFrame.x; - filterArea[3] = currentState.sourceFrame.y; - - shader.uniforms.filterArea = filterArea; - } - - // use this to clamp displaced texture coords so they belong to filterArea - // see displacementFilter fragment shader for an example - if (shader.uniforms.filterClamp) - { - currentState = currentState || this.filterData.stack[this.filterData.index]; - - const filterClamp = shader.uniforms.filterClamp; - - filterClamp[0] = 0; - filterClamp[1] = 0; - filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; - filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; - - shader.uniforms.filterClamp = filterClamp; - } - - // TODO Cacheing layer.. - for (const i in uniformData) - { - if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) - { - if (uniforms[i].baseTexture) - { - shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); - } - else - { - shader.uniforms[i] = textureCount; - - // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; - gl.activeTexture(gl.TEXTURE0 + textureCount); - - uniforms[i].texture.bind(); - } - - textureCount++; - } - else if (uniformData[i].type === 'mat3') - { - // check if its pixi matrix.. - if (uniforms[i].a !== undefined) - { - shader.uniforms[i] = uniforms[i].toArray(true); - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'vec2') - { - // check if its a point.. - if (uniforms[i].x !== undefined) - { - const val = shader.uniforms[i] || new Float32Array(2); - - val[0] = uniforms[i].x; - val[1] = uniforms[i].y; - shader.uniforms[i] = val; - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - else if (uniformData[i].type === 'float') - { - if (shader.uniforms.data[i].value !== uniformData[i]) - { - shader.uniforms[i] = uniforms[i]; - } - } - else - { - shader.uniforms[i] = uniforms[i]; - } - } - } - - /** - * Gets a render target from the pool, or creates a new one. - * - * @param {boolean} clear - Should we clear the render texture when we get it? - * @param {number} resolution - The resolution of the target. - * @return {PIXI.RenderTarget} The new render target - */ - getRenderTarget(clear, resolution) - { - const currentState = this.filterData.stack[this.filterData.index]; - const renderTarget = this.getPotRenderTarget( - this.renderer.gl, - currentState.sourceFrame.width, - currentState.sourceFrame.height, - resolution || currentState.resolution - ); - - renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); - - return renderTarget; - } - - /** - * Returns a render target to the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The render target to return. - */ - returnRenderTarget(renderTarget) - { - this.freePotRenderTarget(renderTarget); - } - - /** - * Calculates the mapped matrix. - * - * TODO playing around here.. this is temporary - (will end up in the shader) - * this returns a matrix that will normalise map filter cords in the filter to screen space - * - * @param {PIXI.Matrix} outputMatrix - the matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size - ); - } - - /** - * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateNormalizedScreenSpaceMatrix(outputMatrix) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateNormalizedScreenSpaceMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - currentState.destinationFrame - ); - } - - /** - * This will map the filter coord so that a texture can be used based on the transform of a sprite - * - * @param {PIXI.Matrix} outputMatrix - The matrix to output to. - * @param {PIXI.Sprite} sprite - The sprite to map to. - * @return {PIXI.Matrix} The mapped matrix. - */ - calculateSpriteMatrix(outputMatrix, sprite) - { - const currentState = this.filterData.stack[this.filterData.index]; - - return filterTransforms.calculateSpriteMatrix( - outputMatrix, - currentState.sourceFrame, - currentState.renderTarget.size, - sprite - ); - } - - /** - * Destroys this Filter Manager. - * - */ - destroy() - { - this.shaderCache = {}; - this.emptyPool(); - } - - /** - * Gets a Power-of-Two render texture. - * - * TODO move to a seperate class could be on renderer? - * also - could cause issue with multiple contexts? - * - * @private - * @param {WebGLRenderingContext} gl - The webgl rendering context - * @param {number} minWidth - The minimum width of the render target. - * @param {number} minHeight - The minimum height of the render target. - * @param {number} resolution - The resolution of the render target. - * @return {PIXI.RenderTarget} The new render target. - */ - getPotRenderTarget(gl, minWidth, minHeight, resolution) - { - // TODO you could return a bigger texture if there is not one in the pool? - minWidth = bitTwiddle.nextPow2(minWidth * resolution); - minHeight = bitTwiddle.nextPow2(minHeight * resolution); - - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - if (!this.pool[key]) - { - this.pool[key] = []; - } - - 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. - renderTarget.resolution = resolution; - renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; - renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; - - return renderTarget; - } - - /** - * Empties the texture pool. - * - */ - emptyPool() - { - for (const i in this.pool) - { - const textures = this.pool[i]; - - if (textures) - { - for (let j = 0; j < textures.length; j++) - { - textures[j].destroy(true); - } - } - } - - this.pool = {}; - } - - /** - * Frees a render target back into the pool. - * - * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free - */ - freePotRenderTarget(renderTarget) - { - const minWidth = renderTarget.size.width * renderTarget.resolution; - const minHeight = renderTarget.size.height * renderTarget.resolution; - const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); - - this.pool[key].push(renderTarget); - } -} diff --git a/src/core/renderers/webgl/managers/FramebufferManager.js b/src/core/renderers/webgl/managers/FramebufferManager.js deleted file mode 100644 index 42c4556..0000000 --- a/src/core/renderers/webgl/managers/FramebufferManager.js +++ /dev/null @@ -1,213 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class FramebufferManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); - } - - // public API - - bind(framebuffer) - { - const gl = this.gl; - - if(framebuffer) - { - // TODO cacheing layer! - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); - // makesure all textures are unbound.. - - // now check for updates... - if(fbo.dirtyId !== framebuffer.dirtyId) - { - fbo.dirtyId = framebuffer.dirtyId; - - if(fbo.dirtyFormat !== framebuffer.dirtyFormat) - { - fbo.dirtyFormat = framebuffer.dirtyFormat; - this.updateFramebuffer(framebuffer); - } - else if(fbo.dirtySize !== framebuffer.dirtySize) - { - fbo.dirtySize = framebuffer.dirtySize; - this.resizeFramebuffer(framebuffer) - } - } - - for (var i = 0; i < framebuffer.colorTextures.length; i++) - { - if(framebuffer.colorTextures[i].texturePart) - { - this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) - } - else - { - this.renderer.texture.unbind(framebuffer.colorTextures[i]) - } - } - - if(framebuffer.depthTexture) - { - this.renderer.texture.unbind(framebuffer.depthTexture); - } - -// gl.viewport(0,0,framebuffer.width, framebuffer.height); - - } - else - { - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - // gl.viewport(0,0,this.renderer.width, this.renderer.height); - } - } - - clear(r, g, b, a) - { - var gl = this.gl; - - // TODO clear color can be set only one right? - gl.clearColor(r, g, b, a); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - } - - // private functions... - - initFramebuffer(framebuffer) - { - const gl = this.gl; - - // TODO - make this a class? - var fbo = { - framebuffer:gl.createFramebuffer(), - stencil:null, - dirtyId:0, - dirtyFormat:0, - dirtySize:0, - } - - framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; - - return fbo; - } - - resizeFramebuffer(framebuffer) - { - const gl = this.gl; - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - if(framebuffer.stencil || framebuffer.depth) - { - gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); - } - } - - updateFramebuffer(framebuffer) - { - const gl = this.gl; - - const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; - - // bind the color texture - const colorTextures = framebuffer.colorTextures; - - let count = colorTextures.length; - - if(!this.drawBufferExtension) - { - count = Math.min(count, 1); - } - - const activeTextures = []; - - for (var i = 0; i < count; i++) - { - let texture = framebuffer.colorTextures[i]; - - if(texture.texturePart) - { - this.renderer.texture.bind(texture.texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, - texture.texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - else - { - this.renderer.texture.bind(texture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0 + i, - gl.TEXTURE_2D, - texture._glTextures[this.CONTEXT_UID].texture, - 0); - } - - activeTextures.push(gl.COLOR_ATTACHMENT0 + i); - } - - if(this.drawBufferExtension && activeTextures.length > 1) - { - this.drawBufferExtension.drawBuffersWEBGL(activeTextures); - } - - if(framebuffer.depthTexture) - { - var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); - - if(depthTextureExt) - { - let depthTexture = framebuffer.depthTexture; - - this.renderer.texture.bind(depthTexture, 0); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, - gl.DEPTH_ATTACHMENT, - gl.TEXTURE_2D, - depthTexture._glTextures[this.CONTEXT_UID].texture, - 0); - } - } - - if(framebuffer.stencil || framebuffer.depth) - { - fbo.stencil = gl.createRenderbuffer(); - - gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); - - // TODO.. this is depth AND stencil? - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); - //fbo.enableStencil(); - } - } - -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/GeometryManager.js b/src/core/renderers/webgl/managers/GeometryManager.js deleted file mode 100644 index af3da72..0000000 --- a/src/core/renderers/webgl/managers/GeometryManager.js +++ /dev/null @@ -1,216 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; -import glCore from 'pixi-gl-core'; - - -const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class GeometryManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this._activeVao = null; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - } - - /** - * Binds geometry so that is can be drawn. Creating a Vao if required - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - */ - bind(geometry, glShader) - { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); - - this.bindVao(vao); - - // TODO - optimise later! - // don't need to loop through if nothing changed! - // maybe look to add an 'autoupdate' to geometry? - for (let i = 0; i < geometry.buffers.length; i++) - { - const buffer = geometry.buffers[i]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - if (buffer._updateID !== glBuffer._updateID) - { - glBuffer._updateID = buffer._updateID; - // TODO - partial upload?? - - glBuffer.upload(buffer.data, 0); - } - } - } - - /** - * Creates a Vao with the same structure as the geometry and stores it on the geometry. - * @private - * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for - * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. - * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. - */ - initGeometryVao(geometry, glShader) - { - const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - - const buffers = geometry.buffers; - const attributes = geometry.attributes; - - // first update - and create the buffers! - for (let i = 0; i < buffers.length; i++) - { - const buffer = buffers[i]; - - if (!buffer._glBuffers[this.CONTEXT_UID]) - { - if (buffer.index) - { - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); - } - else - { - /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); - } - } - } - - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - - const tempStride = {}; - const tempStart = {}; - - for (const j in buffers) - { - tempStride[j] = 0; - tempStart[j] = 0; - } - - for (const j in attributes) - { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; - } - - for (const j in attributes) - { - const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; - - if (attribute.stride === undefined) - { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) - { - attribute.stride = 0; - } - else - { - attribute.stride = tempStride[attribute.buffer]; - } - } - - if (attribute.start === undefined) - { - attribute.start = tempStart[attribute.buffer]; - - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; - } - } - - // next update the attributes buffer.. - for (const j in attributes) - { - const attribute = attributes[j]; - const buffer = buffers[attribute.buffer]; - - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; - - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); - } - - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; - - return vao; - } - - draw(type, size, start, instanceCount) - { - this._activeVao.draw(type, size, start, instanceCount); - } - - /** - * Creates a new VAO from this renderer's context and state. - * - * @return {VertexArrayObject} The new VAO. - */ - createVao() - { - return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); - } - - /** - * Changes the current Vao to the one given in parameter - * - * @param {PIXI.VertexArrayObject} vao - the new Vao - * @return {PIXI.WebGLRenderer} Returns itself. - */ - bindVao(vao) - { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; - } -} diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js deleted file mode 100644 index 1a2ec61..0000000 --- a/src/core/renderers/webgl/managers/MaskManager.js +++ /dev/null @@ -1,199 +0,0 @@ -import WebGLManager from './WebGLManager'; -import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class MaskManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - // TODO - we don't need both! - this.scissor = false; - this.scissorData = null; - this.scissorRenderTarget = null; - - this.enableScissor = true; - - this.alphaMaskPool = []; - this.alphaMaskIndex = 0; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushMask(target, maskData) - { - // TODO the root check means scissor rect will not - // be used on render textures more info here: - // https://github.com/pixijs/pixi.js/pull/3545 - - if (maskData.texture) - { - this.pushSpriteMask(target, maskData); - } - else if (this.enableScissor - && !this.scissor - && this.renderer._activeRenderTarget.root - && !this.renderer.stencil.stencilMaskStack.length - && maskData.isFastRect()) - { - const matrix = maskData.worldTransform; - - let rot = Math.atan2(matrix.b, matrix.a); - - // use the nearest degree! - rot = Math.round(rot * (180 / Math.PI)); - - if (rot % 90) - { - this.pushStencilMask(maskData); - } - else - { - this.pushScissorMask(target, maskData); - } - } - else - { - this.pushStencilMask(maskData); - } - } - - /** - * Removes the last mask from the mask stack and doesn't return it. - * - * @param {PIXI.DisplayObject} target - Display Object to pop the mask from - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - popMask(target, maskData) - { - if (maskData.texture) - { - this.popSpriteMask(target, maskData); - } - else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) - { - this.popScissorMask(target, maskData); - } - else - { - this.popStencilMask(target, maskData); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to - * @param {PIXI.Sprite} maskData - Sprite to be used as the mask - */ - pushSpriteMask(target, maskData) - { - let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; - - if (!alphaMaskFilter) - { - alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; - } - - alphaMaskFilter[0].resolution = this.renderer.resolution; - alphaMaskFilter[0].maskSprite = maskData; - - // TODO - may cause issues! - target.filterArea = maskData.getBounds(true); - - this.renderer.filterManager.pushFilter(target, alphaMaskFilter); - - this.alphaMaskIndex++; - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popSpriteMask() - { - this.renderer.filterManager.popFilter(); - this.alphaMaskIndex--; - } - - /** - * Applies the Mask and adds it to the current filter stack. - * - * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. - */ - pushStencilMask(maskData) - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.pushStencil(maskData); - } - - /** - * Removes the last filter from the filter stack and doesn't return it. - * - */ - popStencilMask() - { - this.renderer.currentRenderer.stop(); - this.renderer.stencil.popStencil(); - } - - /** - * - * @param {PIXI.DisplayObject} target - Display Object to push the mask to - * @param {PIXI.Graphics} maskData - The masking data. - */ - pushScissorMask(target, maskData) - { - maskData.renderable = true; - - const renderTarget = this.renderer._activeRenderTarget; - - const bounds = maskData.getBounds(); - - bounds.fit(renderTarget.size); - maskData.renderable = false; - - this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); - - const resolution = this.renderer.resolution; - - this.renderer.gl.scissor( - bounds.x * resolution, - (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, - bounds.width * resolution, - bounds.height * resolution - ); - - this.scissorRenderTarget = renderTarget; - this.scissorData = maskData; - this.scissor = true; - } - - /** - * - * - */ - popScissorMask() - { - this.scissorRenderTarget = null; - this.scissorData = null; - this.scissor = false; - - // must be scissor! - const gl = this.renderer.gl; - - gl.disable(gl.SCISSOR_TEST); - } -} diff --git a/src/core/renderers/webgl/managers/NewTextureManager.js b/src/core/renderers/webgl/managers/NewTextureManager.js deleted file mode 100644 index 51bcdf0..0000000 --- a/src/core/renderers/webgl/managers/NewTextureManager.js +++ /dev/null @@ -1,244 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class TextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.boundTextures = [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ]; - - this.currentLocation = -1; - } - - /** - * Sets up the renderer context and necessary buffers. - * - * @private - */ - contextChange() - { - const gl = this.gl = this.renderer.gl; - this.CONTEXT_UID = this.renderer.CONTEXT_UID; - - // TODO move this.. to a nice make empty textures class.. - this.emptyTextures = {} - - this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); - this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); - - gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); - - for (var i = 0; i < 6; i++) - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - } - - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - - for (var i = 0; i < this.boundTextures.length; i++) { - this.bind(null, i); - } - } - - bind(texture, location) - { - - const gl = this.gl; - - - location = location || 0; - - if(this.currentLocation !== location) - { - this.currentLocation = location; - gl.activeTexture(gl.TEXTURE0 + location); - } - - if(texture) - { - texture = texture.baseTexture || texture; - - if(texture.valid) - { - - const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); - - gl.bindTexture(texture.target, glTexture.texture); - - if(glTexture.dirtyId !== texture.dirtyId) - { - glTexture.dirtyId = texture.dirtyId; - this.updateTexture(texture); - } - - this.boundTextures[location] = texture; - } - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); - this.boundTextures[location] = null; - } - } - - unbind(texture) - { - const gl = this.gl; - - for (var i = 0; i < this.boundTextures.length; i++) { - - if(this.boundTextures[i] === texture) - { - if(this.currentLocation !== i) - { - gl.activeTexture(gl.TEXTURE0 + i); - this.currentLocation = i; - } - - gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); - this.boundTextures[i] = null; - } - } - } - - initTexture(texture) - { - const gl = this.gl; - - var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); - glTexture.premultiplyAlpha = texture.premultiplyAlpha; - // guarentee an update.. - glTexture.dirtyId = -1; - - texture._glTextures[this.CONTEXT_UID] = glTexture; - - return glTexture; - } - - updateTexture(texture) - { - const glTexture = texture._glTextures[this.CONTEXT_UID]; - const gl = this.gl; - //console.log(gl); - - // TODO there are only 3 textures as far as im aware? - // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) - if(texture.target === gl.TEXTURE_CUBE_MAP) - { - // console.log( gl.UNSIGNED_BYTE) - for (var i = 0; i < texture.sides.length; i++) - { - // TODO - we should only upload what changed.. - // but im sure this is not going to be a problem just yet! - var texturePart = texture.sides[i]; - - if(texturePart.resource) - { - if(texturePart.resource.uploadable) - { - - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.format, - texture.type, - texturePart.resource.source); - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - texturePart.resource.source); - } - } - else - { - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, - 0, - texture.format, - texture.width, - texture.height, - 0, - texture.format, - texture.type, - null); - } - } - } - else - { - if(texture.resource) - { - if(texture.resource.uploadable) - { - glTexture.upload(texture.resource.source); - - } - else - { - glTexture.uploadData(texture.resource.source, texture.width, texture.height); - } - } - else - { - glTexture.uploadData(null, texture.width, texture.height); - } - } - - // lets only update what changes.. - this.setStyle(texture); - } - - setStyle(texture) - { - const gl = this.gl; - - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); - gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); - - if(texture.mipmap) - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - } - else - { - gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } - - gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); - } -} \ No newline at end of file diff --git a/src/core/renderers/webgl/managers/ProjectionManager.js b/src/core/renderers/webgl/managers/ProjectionManager.js deleted file mode 100644 index 752ac4a..0000000 --- a/src/core/renderers/webgl/managers/ProjectionManager.js +++ /dev/null @@ -1,104 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class ProjectionManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.projectionMatrix = new Matrix(); - } - - update(destinationFrame, sourceFrame, root) - { - this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; - - this.calculateProjection(this.destinationFrame, this.sourceFrame, root); - - this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; - this.renderer.globalUniforms.update(); - - const gl = this.renderer.gl; - - // TODO this is bot needed here? - const resolution = 1; - - // TODO add a check as them may be the same! - if (this.destinationFrame !== this.sourceFrame) - { -// gl.enable(gl.SCISSOR_TEST); - gl.scissor( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - else - { - // gl.disable(gl.SCISSOR_TEST); - } - - // TODO - does not need to be updated all the time?? - gl.viewport( - this.destinationFrame.x | 0, - this.destinationFrame.y | 0, - (this.destinationFrame.width * resolution) | 0, - (this.destinationFrame.height * resolution) | 0 - ); - } - - /** - * Updates the projection matrix based on a projection frame (which is a rectangle) - * - * @param {Rectangle} destinationFrame - The destination frame. - * @param {Rectangle} sourceFrame - The source frame. - */ - calculateProjection(destinationFrame, sourceFrame, root) - { - const pm = this.projectionMatrix; - - pm.identity(); - - // TODO: make dest scale source - if (!root) - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = 1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = -1 - (sourceFrame.y * pm.d); - } - else - { - pm.a = 1 / destinationFrame.width * 2; - pm.d = -1 / destinationFrame.height * 2; - - pm.tx = -1 - (sourceFrame.x * pm.a); - pm.ty = 1 - (sourceFrame.y * pm.d); - } - } - - - /** - * Sets the transform of the active render target to the given matrix - * - * @param {PIXI.Matrix} matrix - The transformation matrix - */ - setTransform(matrix) - { - // this._activeRenderTarget.transform = matrix; - } - -} diff --git a/src/core/renderers/webgl/managers/RenderTextureManager.js b/src/core/renderers/webgl/managers/RenderTextureManager.js deleted file mode 100644 index e88b421..0000000 --- a/src/core/renderers/webgl/managers/RenderTextureManager.js +++ /dev/null @@ -1,79 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { Rectangle, Matrix } from '../../../math'; - -const tempRect = new Rectangle(); - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ - -export default class RenderTextureManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.clearColor = renderer._backgroundColorRgba; - } - - bind(renderTexture) - { - // TODO - do we want this?? - if(this.renderTexture === renderTexture)return; - - this.renderTexture = renderTexture; - - if(renderTexture) - { - this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); - this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); - } - else - { - this.renderer.framebuffer.bind(null); - - tempRect.width = this.renderer.width; - tempRect.height = this.renderer.height; - // TODO store this.. - this.renderer.projection.update(tempRect, tempRect, true); - } - - const glShader = this.renderer.shader.getGLShader() - - if (glShader) - { - // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); - } - } - - /** - * Erases the render texture and fills the drawing area with a colour - * - * @param {number} [clearColor] - The colour - * @return {PIXI.WebGLRenderer} Returns itself. - */ - clear(clearColor) - { - if(this.renderTexture) - { - clearColor = clearColor || this.renderTexture.baseTexture.clearColor; - } - else - { - clearColor = clearColor || this.clearColor; - } - - this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - - resize(screenWidth, screenHeight) - { - // resize the root only! - this.bind(null) - } -} diff --git a/src/core/renderers/webgl/managers/ShaderManager.js b/src/core/renderers/webgl/managers/ShaderManager.js deleted file mode 100644 index 1c4f5f1..0000000 --- a/src/core/renderers/webgl/managers/ShaderManager.js +++ /dev/null @@ -1,165 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GLShader } from 'pixi-gl-core'; -import { PRECISION } from '../../../const'; -import generateUniformsSync from '../../../shader/generateUniformsSync2'; - -let UID = 0; - -/** - * Helper class to create a webGL Texture - * - * @class - * @memberof PIXI - */ -export default class ShaderManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer - */ - constructor(renderer) - { - - super(renderer); - - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = null; - - this.shader = null; - - this.id = UID++; - } - - contextChange(gl) - { - this.gl = gl; - } - - /** - * Changes the current shader to the one given in parameter - * - * @param {PIXI.Shader} shader - the new shader - * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. - * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. - */ - bind(shader, dontSync) - { - const program = shader.program; - const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); - - // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. - // if (this.shader !== shader) - // { - if (this.shader !== shader) - { - this.shader = shader; - glShader.bind(); - } - - if (!dontSync) - { - this.syncUniformGroup(shader.uniformGroup); - } - - return glShader; - } - - /** - * Uploads the uniforms values to the currently bound shader. - * - * @param {object} uniforms - the uniforms valiues that be applied to the current shader - */ - setUniforms(uniforms) - { - const shader = this.shader.program; - const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; - - shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); - } - - setUniformsGroups(uniformGroups) - { - - const glShader = this.getGLShader(); - - const group = uniformGroups[0]; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - - } - - syncUniformGroup(group) - { - const glShader = this.getGLShader(); - - if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) - { - glShader.uniformGroups[group.id] = group.dirtyId; - const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); - - syncFunc(glShader.uniformData, group.uniforms, this.renderer); - } - - } - - createSynGroups(group) - { - group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); - - return group.syncUniforms[this.shader.program.id]; - } - /** - * Returns the underlying GLShade rof the currently bound shader. - * This can be handy for when you to have a little more control over the setting of your uniforms. - * - * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context - */ - getGLShader() - { - if(this.shader) - { - return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; - } - else - { - return null; - } - } - - /** - * Generates a GLShader verion of the Shader provided. - * - * @private - * @param {PIXI.Shader} shader the shader that the glShader will be based on. - * @return {PIXI.glCore.GLShader} A shiney new GLShader - */ - generateShader(shader) - { - const program = shader.program; - const attribMap = {}; - - for (const i in program.attributeData) - { - attribMap[i] = program.attributeData[i].location; - } - - const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - - program.glShaders[this.renderer.CONTEXT_UID] = glShader; - - return glShader; - } - - /** - * Destroys this manager and removes all its textures - */ - destroy() - { - // TODO implement destroy method for ShaderManager - this.destroyed = true; - } -} diff --git a/src/core/renderers/webgl/managers/StateManager.js b/src/core/renderers/webgl/managers/StateManager.js deleted file mode 100755 index cc10a28..0000000 --- a/src/core/renderers/webgl/managers/StateManager.js +++ /dev/null @@ -1,289 +0,0 @@ -import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; -import WebGLManager from './WebGLManager'; -import WebGLState from '../State'; - -const BLEND = 0; -const OFFSET = 1; -const CULLING = 2; -const DEPTH_TEST = 3; -const WINDING = 4; - -/** - * A WebGL state machines - * - * @memberof PIXI - * @class - */ -export default class StateManager extends WebGLManager -{ - /** - * @param {WebGLRenderingContext} gl - The current WebGL rendering context - */ - constructor(renderer) - { - super(renderer); - - this.gl = null; - - this.maxAttribs = null; - - // check we have vao.. - this.nativeVaoExtension = null; - - this.attribState = null; - - this.stateId = 0; - this.polygonOffset = 0; - this.blendMode = 17; - - this.map = []; - - // map functions for when we set state.. - this.map[BLEND] = this.setBlend; - this.map[OFFSET] = this.setOffset; - this.map[CULLING] = this.setCullFace; - this.map[DEPTH_TEST] = this.setDepthTest; - this.map[WINDING] = this.setFrontFace; - - this.checks = []; - - this.defaultState = new WebGLState(); - this.defaultState.blend = true; - this.defaultState.depth = true; - - - } - - contextChange(gl) - { - /** - * The current WebGL rendering context - * - * @member {WebGLRenderingContext} - */ - this.gl = gl; - - this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); - - // check we have vao.. - this.nativeVaoExtension = ( - gl.getExtension('OES_vertex_array_object') - || gl.getExtension('MOZ_OES_vertex_array_object') - || gl.getExtension('WEBKIT_OES_vertex_array_object') - ); - - this.attribState = { - tempAttribState: new Array(this.maxAttribs), - attribState: new Array(this.maxAttribs), - }; - - this.blendModes = mapWebGLBlendModesToPixi(gl); - - this.setState(this.defaultState); - - this.reset(); - } - - /** - * Sets the current state - * - * @param {*} state - The state to set. - */ - setState(state) - { - state = state || this.defaultState; - - // TODO maybe to an object check? ( this.state === state )? - if (this.stateId !== state.data) - { - let diff = this.stateId ^ state.data; - let i = 0; - - // order from least to most common - while (diff) - { - if (diff & 1) - { - // state change! - this.map[i].call(this, !!(state.data & (1 << i))); - } - - diff = diff >> 1; - i++; - } - - this.stateId = state.data; - } - - // based on the above settings we check for specific modes.. - // for example if blend is active we check and set the blend modes - // or of polygon offset is active we check the poly depth. - for (let i = 0; i < this.checks.length; i++) - { - this.checks[i](this, state); - } - } - - /** - * Enables or disabled blending. - * - * @param {boolean} value - Turn on or off webgl blending. - */ - setBlend(value) - { - this.updateCheck(StateManager.checkBlendMode, value); - - this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); - } - - /** - * Enables or disable polygon offset fill - * - * @param {boolean} value - Turn on or off webgl polygon offset testing. - */ - setOffset(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); - } - - /** - * Sets whether to enable or disable depth test. - * - * @param {boolean} value - Turn on or off webgl depth testing. - */ - setDepthTest(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); - } - - /** - * Sets whether to enable or disable cull face. - * - * @param {boolean} value - Turn on or off webgl cull face. - */ - setCullFace(value) - { - this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); - } - - /** - * Sets the gl front face. - * - * @param {boolean} value - true is clockwise and false is counter-clockwise - */ - setFrontFace(value) - { - this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); - } - - /** - * Sets the blend mode. - * - * @param {number} value - The blend mode to set to. - */ - setBlendMode(value) - { - if (value === this.blendMode) - { - return; - } - - this.blendMode = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); - } - - /** - * Sets the polygon offset. - * - * @param {number} value - the polygon offset - * @param {number} scale - the polygon offset scale - */ - setPolygonOffset(value, scale) - { - this.gl.polygonOffset(value, scale); - } - - /** - * Disables all the vaos in use - * - */ - resetAttributes() - { - for (let i = 0; i < this.attribState.tempAttribState.length; i++) - { - this.attribState.tempAttribState[i] = 0; - } - - for (let i = 0; i < this.attribState.attribState.length; i++) - { - this.attribState.attribState[i] = 0; - } - - // im going to assume one is always active for performance reasons. - for (let i = 1; i < this.maxAttribs; i++) - { - this.gl.disableVertexAttribArray(i); - } - } - - // used - /** - * Resets all the logic and disables the vaos - */ - reset() - { - // unbind any VAO if they exist.. - if (this.nativeVaoExtension) - { - this.nativeVaoExtension.bindVertexArrayOES(null); - } - - // reset all attributes.. - this.resetAttributes(); - - this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); - - this.setBlendMode(0); - - // TODO? - // this.setState(this.defaultState); - } - - /** - * checks to see which updates should be checked based on which settings have been activated - * for example if blend is enabled then we shold check the blend modes each time the state is changed - * or if poygon fill is activated then we need to check if the polygone offset changes. - * The idea is that we only check what we have too. - * - * @param {Function} func the checking function to add or remove - * @param {boolean} value should the check function be added or removed. - */ - updateCheck(func, value) - { - const index = this.checks.indexOf(func); - - if (value && index === -1) - { - this.checks.push(func); - } - else if (!value && index !== -1) - { - this.checks.splice(index, 1); - } - } - - /** - * A private little wrapper function that we call to check the blend mode. - * - * @static - * @private - * @param {PIXI.StateManager} manager the manager to perform the state check on - * @param {PIXI.State} state the state that the blendMode will pulled from - */ - static checkBlendMode(manager, state) - { - manager.setBlendMode(state.blendMode); - } - - // TODO - add polygon offset? -} diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js deleted file mode 100644 index 70ff230..0000000 --- a/src/core/renderers/webgl/managers/StencilManager.js +++ /dev/null @@ -1,113 +0,0 @@ -import WebGLManager from './WebGLManager'; - -/** - * @class - * @extends PIXI.WebGLManager - * @memberof PIXI - */ -export default class StencilManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - this.stencilMaskStack = null; - } - - /** - * Changes the mask stack that is used by this manager. - * - * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack - */ - setMaskStack(stencilMaskStack) - { - this.stencilMaskStack = stencilMaskStack; - - const gl = this.renderer.gl; - - if (stencilMaskStack.length === 0) - { - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.enable(gl.STENCIL_TEST); - } - } - - /** - * Applies the Mask and adds it to the current filter stack. @alvin - * - * @param {PIXI.Graphics} graphics - The mask - */ - pushStencil(graphics) - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - this.renderer._activeRenderTarget.attachStencilBuffer(); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - if (sms.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - gl.stencilFunc(gl.ALWAYS, 1, 1); - } - - sms.push(graphics); - - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - - /** - * TODO @alvin - */ - popStencil() - { - this.renderer.setObjectRenderer(this.renderer.plugins.graphics); - - const gl = this.renderer.gl; - const sms = this.stencilMaskStack; - - const graphics = sms.pop(); - - if (sms.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - } - else - { - gl.colorMask(false, false, false, false); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); - - this.renderer.plugins.graphics.render(graphics); - - gl.colorMask(true, true, true, true); - gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); - gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); - } - } - - /** - * Destroys the mask stack. - * - */ - destroy() - { - WebGLManager.prototype.destroy.call(this); - - this.stencilMaskStack.stencilStack = null; - } -} diff --git a/src/core/renderers/webgl/managers/TextureGCManager.js b/src/core/renderers/webgl/managers/TextureGCManager.js deleted file mode 100644 index c7c93b3..0000000 --- a/src/core/renderers/webgl/managers/TextureGCManager.js +++ /dev/null @@ -1,111 +0,0 @@ -import WebGLManager from './WebGLManager'; -import { GC_MODES } from '../../../const'; -import settings from '../../../settings'; - -/** - * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged - * up with textures that are no longer being used. - * - * @class - * @memberof PIXI - */ -export default class TextureGCManager extends WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - super(renderer); - - this.count = 0; - this.checkCount = 0; - this.maxIdle = settings.GC_MAX_IDLE; - this.checkCountMax = settings.GC_MAX_CHECK_COUNT; - this.mode = settings.GC_MODE; - } - - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - postrender() - { - this.count++; - - if (this.mode === GC_MODES.MANUAL) - { - return; - } - - this.checkCount++; - - if (this.checkCount > this.checkCountMax) - { - this.checkCount = 0; - - this.run(); - } - } - - /** - * Checks to see when the last time a texture was used - * if the texture has not been used for a specified amount of time it will be removed from the GPU - */ - run() - { - const tm = this.renderer.textureManager; - const managedTextures = tm._managedTextures; - let wasRemoved = false; - - for (let i = 0; i < managedTextures.length; i++) - { - const texture = managedTextures[i]; - - // only supports non generated textures at the moment! - if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) - { - tm.destroyTexture(texture, true); - managedTextures[i] = null; - wasRemoved = true; - } - } - - if (wasRemoved) - { - let j = 0; - - for (let i = 0; i < managedTextures.length; i++) - { - if (managedTextures[i] !== null) - { - managedTextures[j++] = managedTextures[i]; - } - } - - managedTextures.length = j; - } - } - - /** - * Removes all the textures within the specified displayObject and its children from the GPU - * - * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. - */ - unload(displayObject) - { - const tm = this.renderer.textureManager; - - // only destroy non generated textures - if (displayObject._texture && displayObject._texture._glRenderTargets) - { - tm.destroyTexture(displayObject._texture, true); - } - - for (let i = displayObject.children.length - 1; i >= 0; i--) - { - this.unload(displayObject.children[i]); - } - } -} diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js deleted file mode 100644 index 59e4ba8..0000000 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @class - * @memberof PIXI - */ -export default class WebGLManager -{ - /** - * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. - */ - constructor(renderer) - { - /** - * The renderer this manager works for. - * - * @member {PIXI.WebGLRenderer} - */ - this.renderer = renderer; - - this.renderer.runners.contextChange.add(this); - } - - /** - * Generic method called when there is a WebGL context change. - * - */ - contextChange() - { - // do some codes init! - } - - /** - * Generic destroy methods to be overridden by the subclass - * - */ - destroy() - { - this.renderer.runners.contextChange.remove(this); - this.renderer = null; - } -} diff --git a/src/core/renderers/webgl/systems/BatchSystem.js b/src/core/renderers/webgl/systems/BatchSystem.js new file mode 100644 index 0000000..a01cc0b --- /dev/null +++ b/src/core/renderers/webgl/systems/BatchSystem.js @@ -0,0 +1,69 @@ +import WebGLSystem from './WebGLSystem'; +import ObjectRenderer from '../utils/ObjectRenderer'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class BatchSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + super(renderer); + + /** + * An empty renderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.emptyRenderer = new ObjectRenderer(renderer); + + /** + * The currently active ObjectRenderer. + * + * @member {PIXI.ObjectRenderer} + */ + this.currentRenderer = this.emptyRenderer; + } + + /** + * Changes the current renderer to the one given in parameter + * + * @param {PIXI.ObjectRenderer} objectRenderer - The object renderer to use. + */ + setObjectRenderer(objectRenderer) + { + if (this.currentRenderer === objectRenderer) + { + return; + } + + this.currentRenderer.stop(); + this.currentRenderer = objectRenderer; + + this.renderer.state.setState(objectRenderer.state); + + this.currentRenderer.start(); + } + + /** + * This should be called if you wish to do some custom rendering + * It will basically render anything that may be batched up such as sprites + * + */ + flush() + { + this.setObjectRenderer(this.emptyRenderer); + } + + reset() + { + this.setObjectRenderer(this.emptyRenderer); + } +} diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js new file mode 100644 index 0000000..fa26c42 --- /dev/null +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -0,0 +1,119 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; +import validateContext from '../utils/validateContext'; + +let CONTEXT_UID = 0; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ContextSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.handleContextLost = this.handleContextLost.bind(this); + this.handleContextRestored = this.handleContextRestored.bind(this); + + renderer.view.addEventListener('webglcontextlost', this.handleContextLost, false); + renderer.view.addEventListener('webglcontextrestored', this.handleContextRestored, false); + + } + + get isLost() + { + return (!this.gl || this.gl.isContextLost()); + } + + contextChange(gl) + { + this.gl = gl; + + // restore a context if it was previously lost + if (gl.isContextLost() && gl.getExtension('WEBGL_lose_context')) + { + gl.getExtension('WEBGL_lose_context').restoreContext(); + } + + // set the latest testing context.. + if(!glCore._testingContext) + { + glCore._testingContext = gl; + } + + // setup the width/height properties and gl viewport + //this.resize(this.screen.width, this.screen.height); + // const renderer = this.renderer; + + // renderer.resize(renderer.screen.width, renderer.screen.height); + } + + initFromContext(gl) + { + this.gl = gl; + validateContext(gl); + this.renderer.gl = gl; + this.renderer.CONTEXT_UID = CONTEXT_UID++; + this.renderer.runners.contextChange.run(gl); + } + + initFromOptions(options) + { + const gl = glCore.createContext(this.renderer.view, this.options); + this.initFromContext(gl); + } + + /** + * Handles a lost webgl context + * + * @private + * @param {WebGLContextEvent} event - The context lost event. + */ + handleContextLost(event) + { + event.preventDefault(); + } + + /** + * Handles a restored webgl context + * + * @private + */ + handleContextRestored() + { + this.renderer.runners.contextChange.run(gl); + + // TODO - tidy up textures? + //this.textureSystem.removeAll(); + } + + destroy() + { + const view = this.renderer.view; + + // remove listeners + view.removeEventListener('webglcontextlost', this.handleContextLost); + view.removeEventListener('webglcontextrestored', this.handleContextRestored); + + this.gl.useProgram(null); + + if (this.gl.getExtension('WEBGL_lose_context')) + { + this.gl.getExtension('WEBGL_lose_context').loseContext(); + } + + } + + postrender() + { + this.gl.flush(); + } +} diff --git a/src/core/renderers/webgl/systems/FilterSystem.js b/src/core/renderers/webgl/systems/FilterSystem.js new file mode 100644 index 0000000..f47335d --- /dev/null +++ b/src/core/renderers/webgl/systems/FilterSystem.js @@ -0,0 +1,588 @@ +import WebGLSystem from './WebGLSystem'; +import RenderTarget from '../utils/RenderTarget'; +import Quad from '../utils/Quad'; +import { Rectangle } from '../../../math'; +//import Shader from '../../../Shader'; +import * as filterTransforms from '../filters/filterTransforms'; +import bitTwiddle from 'bit-twiddle'; + +/** + * @ignore + * @class + */ +class FilterState +{ + /** + * + */ + constructor() + { + this.renderTarget = null; + this.sourceFrame = new Rectangle(); + this.destinationFrame = new Rectangle(); + this.filters = []; + this.target = null; + this.resolution = 1; + } +} + +/** + * @class + * @memberof PIXI + * @extends PIXI.WebGLSystem + */ +export default class FilterSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + + this.shaderCache = {}; + // todo add default! + this.pool = {}; + + this.filterData = null; + } + + contextChange() + { + this.gl = this.renderer.gl; + // know about sprites! + this.quad = new Quad(this.gl, this.renderer.state.attribState); + + } + + /** + * Adds a new filter to the System. + * + * @param {PIXI.DisplayObject} target - The target of the filter to render. + * @param {PIXI.Filter[]} filters - The filters to apply. + */ + pushFilter(target, filters) + { + const renderer = this.renderer; + + let filterData = this.filterData; + + if (!filterData) + { + filterData = this.renderer._activeRenderTarget.filterStack; + + // add new stack + const filterState = new FilterState(); + + filterState.sourceFrame = filterState.destinationFrame = this.renderer._activeRenderTarget.size; + filterState.renderTarget = renderer._activeRenderTarget; + + this.renderer._activeRenderTarget.filterData = filterData = { + index: 0, + stack: [filterState], + }; + + this.filterData = filterData; + } + + // get the current filter state.. + let currentState = filterData.stack[++filterData.index]; + + if (!currentState) + { + currentState = filterData.stack[filterData.index] = new FilterState(); + } + + // for now we go off the filter of the first resolution.. + const resolution = filters[0].resolution; + const padding = filters[0].padding | 0; + const targetBounds = target.filterArea || target.getBounds(true); + const sourceFrame = currentState.sourceFrame; + const destinationFrame = currentState.destinationFrame; + + sourceFrame.x = ((targetBounds.x * resolution) | 0) / resolution; + sourceFrame.y = ((targetBounds.y * resolution) | 0) / resolution; + sourceFrame.width = ((targetBounds.width * resolution) | 0) / resolution; + sourceFrame.height = ((targetBounds.height * resolution) | 0) / resolution; + + if (filterData.stack[0].renderTarget.transform) + { // + + // TODO we should fit the rect around the transform.. + } + else + { + sourceFrame.fit(filterData.stack[0].destinationFrame); + } + + // lets apply the padding After we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + sourceFrame.pad(padding); + + destinationFrame.width = sourceFrame.width; + destinationFrame.height = sourceFrame.height; + + // lets play the padding after we fit the element to the screen. + // this should stop the strange side effects that can occur when cropping to the edges + + const renderTarget = this.getPotRenderTarget(renderer.gl, sourceFrame.width, sourceFrame.height, resolution); + + currentState.target = target; + currentState.filters = filters; + currentState.resolution = resolution; + currentState.renderTarget = renderTarget; + + // bind the render target to draw the shape in the top corner.. + + renderTarget.setFrame(destinationFrame, sourceFrame); + + // bind the render target + renderer.bindRenderTarget(renderTarget); + renderTarget.clear(); + } + + /** + * Pops off the filter and applies it. + * + */ + popFilter() + { + const filterData = this.filterData; + + const lastState = filterData.stack[filterData.index - 1]; + const currentState = filterData.stack[filterData.index]; + + this.quad.map(currentState.renderTarget.size, currentState.sourceFrame).upload(); + + const filters = currentState.filters; + + if (filters.length === 1) + { + filters[0].apply(this, currentState.renderTarget, lastState.renderTarget, false, currentState); + this.freePotRenderTarget(currentState.renderTarget); + } + else + { + let flip = currentState.renderTarget; + let flop = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + currentState.resolution + ); + + flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + // finally lets clear the render target before drawing to it.. + flop.clear(); + + let i = 0; + + for (i = 0; i < filters.length - 1; ++i) + { + filters[i].apply(this, flip, flop, true, currentState); + + const t = flip; + + flip = flop; + flop = t; + } + + filters[i].apply(this, flip, lastState.renderTarget, false, currentState); + + this.freePotRenderTarget(flip); + this.freePotRenderTarget(flop); + } + + filterData.index--; + + if (filterData.index === 0) + { + this.filterData = null; + } + } + + /** + * Draws a filter. + * + * @param {PIXI.Filter} filter - The filter to draw. + * @param {PIXI.RenderTarget} input - The input render target. + * @param {PIXI.RenderTarget} output - The target to output to. + * @param {boolean} clear - Should the output be cleared before rendering to it + */ + applyFilter(filter, input, output, clear) + { + const renderer = this.renderer; + const gl = renderer.gl; + + let shader = filter.glShaders[renderer.CONTEXT_UID]; + + // cacheing.. + if (!shader) + { + if (filter.glShaderKey) + { + shader = this.shaderCache[filter.glShaderKey]; + + if (!shader) + { + shader = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + + filter.glShaders[renderer.CONTEXT_UID] = this.shaderCache[filter.glShaderKey] = shader; + } + } + else + { + shader = filter.glShaders[renderer.CONTEXT_UID] = new Shader(this.gl, filter.vertexSrc, filter.fragmentSrc); + } + + // TODO - this only needs to be done once? + renderer.bindVao(null); + + this.quad.initVao(shader); + } + + renderer.bindVao(this.quad.vao); + + renderer.bindRenderTarget(output); + + if (clear) + { + gl.disable(gl.SCISSOR_TEST); + renderer.clear();// [1, 1, 1, 1]); + gl.enable(gl.SCISSOR_TEST); + } + + // in case the render target is being masked using a scissor rect + if (output === renderer.maskSystem.scissorRenderTarget) + { + renderer.maskSystem.pushScissorMask(null, renderer.maskSystem.scissorData); + } + + renderer.bindShader(shader); + + // free unit 0 for us, doesn't matter what was there + // don't try to restore it, because syncUniforms can upload it to another slot + // and it'll be a problem + const tex = this.renderer.emptyTextures[0]; + + this.renderer.boundTextures[0] = tex; + // this syncs the pixi filters uniforms with glsl uniforms + this.syncUniforms(shader, filter); + + renderer.state.setBlendMode(filter.blendMode); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, input.texture.texture); + + this.quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); + + gl.bindTexture(gl.TEXTURE_2D, tex._glTextures[this.renderer.CONTEXT_UID].texture); + } + + /** + * Uploads the uniforms of the filter. + * + * @param {GLShader} shader - The underlying gl shader. + * @param {PIXI.Filter} filter - The filter we are synchronizing. + */ + syncUniforms(shader, filter) + { + const uniformData = filter.uniformData; + const uniforms = filter.uniforms; + + // 0 is reserved for the pixi texture so we start at 1! + let textureCount = 1; + let currentState; + + // filterArea and filterClamp that are handled by FilterSystem directly + // they must not appear in uniformData + + if (shader.uniforms.filterArea) + { + currentState = this.filterData.stack[this.filterData.index]; + + const filterArea = shader.uniforms.filterArea; + + filterArea[0] = currentState.renderTarget.size.width; + filterArea[1] = currentState.renderTarget.size.height; + filterArea[2] = currentState.sourceFrame.x; + filterArea[3] = currentState.sourceFrame.y; + + shader.uniforms.filterArea = filterArea; + } + + // use this to clamp displaced texture coords so they belong to filterArea + // see displacementFilter fragment shader for an example + if (shader.uniforms.filterClamp) + { + currentState = currentState || this.filterData.stack[this.filterData.index]; + + const filterClamp = shader.uniforms.filterClamp; + + filterClamp[0] = 0; + filterClamp[1] = 0; + filterClamp[2] = (currentState.sourceFrame.width - 1) / currentState.renderTarget.size.width; + filterClamp[3] = (currentState.sourceFrame.height - 1) / currentState.renderTarget.size.height; + + shader.uniforms.filterClamp = filterClamp; + } + + // TODO Cacheing layer.. + for (const i in uniformData) + { + if (uniformData[i].type === 'sampler2D' && uniforms[i] !== 0) + { + if (uniforms[i].baseTexture) + { + shader.uniforms[i] = this.renderer.bindTexture(uniforms[i].baseTexture, textureCount); + } + else + { + shader.uniforms[i] = textureCount; + + // 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.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; + gl.activeTexture(gl.TEXTURE0 + textureCount); + + uniforms[i].texture.bind(); + } + + textureCount++; + } + else if (uniformData[i].type === 'mat3') + { + // check if its pixi matrix.. + if (uniforms[i].a !== undefined) + { + shader.uniforms[i] = uniforms[i].toArray(true); + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'vec2') + { + // check if its a point.. + if (uniforms[i].x !== undefined) + { + const val = shader.uniforms[i] || new Float32Array(2); + + val[0] = uniforms[i].x; + val[1] = uniforms[i].y; + shader.uniforms[i] = val; + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + else if (uniformData[i].type === 'float') + { + if (shader.uniforms.data[i].value !== uniformData[i]) + { + shader.uniforms[i] = uniforms[i]; + } + } + else + { + shader.uniforms[i] = uniforms[i]; + } + } + } + + /** + * Gets a render target from the pool, or creates a new one. + * + * @param {boolean} clear - Should we clear the render texture when we get it? + * @param {number} resolution - The resolution of the target. + * @return {PIXI.RenderTarget} The new render target + */ + getRenderTarget(clear, resolution) + { + const currentState = this.filterData.stack[this.filterData.index]; + const renderTarget = this.getPotRenderTarget( + this.renderer.gl, + currentState.sourceFrame.width, + currentState.sourceFrame.height, + resolution || currentState.resolution + ); + + renderTarget.setFrame(currentState.destinationFrame, currentState.sourceFrame); + + return renderTarget; + } + + /** + * Returns a render target to the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The render target to return. + */ + returnRenderTarget(renderTarget) + { + this.freePotRenderTarget(renderTarget); + } + + /** + * Calculates the mapped matrix. + * + * TODO playing around here.. this is temporary - (will end up in the shader) + * this returns a matrix that will normalise map filter cords in the filter to screen space + * + * @param {PIXI.Matrix} outputMatrix - the matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size + ); + } + + /** + * Multiply vTextureCoord to this matrix to achieve (0,0,1,1) for filterArea + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateNormalizedScreenSpaceMatrix(outputMatrix) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateNormalizedScreenSpaceMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + currentState.destinationFrame + ); + } + + /** + * This will map the filter coord so that a texture can be used based on the transform of a sprite + * + * @param {PIXI.Matrix} outputMatrix - The matrix to output to. + * @param {PIXI.Sprite} sprite - The sprite to map to. + * @return {PIXI.Matrix} The mapped matrix. + */ + calculateSpriteMatrix(outputMatrix, sprite) + { + const currentState = this.filterData.stack[this.filterData.index]; + + return filterTransforms.calculateSpriteMatrix( + outputMatrix, + currentState.sourceFrame, + currentState.renderTarget.size, + sprite + ); + } + + /** + * Destroys this Filter System. + * + */ + destroy() + { + this.shaderCache = {}; + this.emptyPool(); + } + + /** + * Gets a Power-of-Two render texture. + * + * TODO move to a seperate class could be on renderer? + * also - could cause issue with multiple contexts? + * + * @private + * @param {WebGLRenderingContext} gl - The webgl rendering context + * @param {number} minWidth - The minimum width of the render target. + * @param {number} minHeight - The minimum height of the render target. + * @param {number} resolution - The resolution of the render target. + * @return {PIXI.RenderTarget} The new render target. + */ + getPotRenderTarget(gl, minWidth, minHeight, resolution) + { + // TODO you could return a bigger texture if there is not one in the pool? + minWidth = bitTwiddle.nextPow2(minWidth * resolution); + minHeight = bitTwiddle.nextPow2(minHeight * resolution); + + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + if (!this.pool[key]) + { + this.pool[key] = []; + } + + 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. + renderTarget.resolution = resolution; + renderTarget.defaultFrame.width = renderTarget.size.width = minWidth / resolution; + renderTarget.defaultFrame.height = renderTarget.size.height = minHeight / resolution; + + return renderTarget; + } + + /** + * Empties the texture pool. + * + */ + emptyPool() + { + for (const i in this.pool) + { + const textures = this.pool[i]; + + if (textures) + { + for (let j = 0; j < textures.length; j++) + { + textures[j].destroy(true); + } + } + } + + this.pool = {}; + } + + /** + * Frees a render target back into the pool. + * + * @param {PIXI.RenderTarget} renderTarget - The renderTarget to free + */ + freePotRenderTarget(renderTarget) + { + const minWidth = renderTarget.size.width * renderTarget.resolution; + const minHeight = renderTarget.size.height * renderTarget.resolution; + const key = ((minWidth & 0xFFFF) << 16) | (minHeight & 0xFFFF); + + this.pool[key].push(renderTarget); + } +} diff --git a/src/core/renderers/webgl/systems/FramebufferSystem.js b/src/core/renderers/webgl/systems/FramebufferSystem.js new file mode 100644 index 0000000..70408af --- /dev/null +++ b/src/core/renderers/webgl/systems/FramebufferSystem.js @@ -0,0 +1,213 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class FramebufferSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + this.drawBufferExtension = this.gl.getExtension('WEBGL_draw_buffers'); + } + + // public API + + bind(framebuffer) + { + const gl = this.gl; + + if(framebuffer) + { + // TODO cacheing layer! + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID] || this.initFramebuffer(framebuffer); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo.framebuffer); + // makesure all textures are unbound.. + + // now check for updates... + if(fbo.dirtyId !== framebuffer.dirtyId) + { + fbo.dirtyId = framebuffer.dirtyId; + + if(fbo.dirtyFormat !== framebuffer.dirtyFormat) + { + fbo.dirtyFormat = framebuffer.dirtyFormat; + this.updateFramebuffer(framebuffer); + } + else if(fbo.dirtySize !== framebuffer.dirtySize) + { + fbo.dirtySize = framebuffer.dirtySize; + this.resizeFramebuffer(framebuffer) + } + } + + for (var i = 0; i < framebuffer.colorTextures.length; i++) + { + if(framebuffer.colorTextures[i].texturePart) + { + this.renderer.texture.unbind(framebuffer.colorTextures[i].texture) + } + else + { + this.renderer.texture.unbind(framebuffer.colorTextures[i]) + } + } + + if(framebuffer.depthTexture) + { + this.renderer.texture.unbind(framebuffer.depthTexture); + } + +// gl.viewport(0,0,framebuffer.width, framebuffer.height); + + } + else + { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // gl.viewport(0,0,this.renderer.width, this.renderer.height); + } + } + + clear(r, g, b, a) + { + var gl = this.gl; + + // TODO clear color can be set only one right? + gl.clearColor(r, g, b, a); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + // private functions... + + initFramebuffer(framebuffer) + { + const gl = this.gl; + + // TODO - make this a class? + var fbo = { + framebuffer:gl.createFramebuffer(), + stencil:null, + dirtyId:0, + dirtyFormat:0, + dirtySize:0, + } + + framebuffer.glFrameBuffers[this.CONTEXT_UID] = fbo; + + return fbo; + } + + resizeFramebuffer(framebuffer) + { + const gl = this.gl; + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + if(framebuffer.stencil || framebuffer.depth) + { + gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width, framebuffer.height); + } + } + + updateFramebuffer(framebuffer) + { + const gl = this.gl; + + const fbo = framebuffer.glFrameBuffers[this.CONTEXT_UID]; + + // bind the color texture + const colorTextures = framebuffer.colorTextures; + + let count = colorTextures.length; + + if(!this.drawBufferExtension) + { + count = Math.min(count, 1); + } + + const activeTextures = []; + + for (var i = 0; i < count; i++) + { + let texture = framebuffer.colorTextures[i]; + + if(texture.texturePart) + { + this.renderer.texture.bind(texture.texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X + texture.side, + texture.texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + else + { + this.renderer.texture.bind(texture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0 + i, + gl.TEXTURE_2D, + texture._glTextures[this.CONTEXT_UID].texture, + 0); + } + + activeTextures.push(gl.COLOR_ATTACHMENT0 + i); + } + + if(this.drawBufferExtension && activeTextures.length > 1) + { + this.drawBufferExtension.drawBuffersWEBGL(activeTextures); + } + + if(framebuffer.depthTexture) + { + var depthTextureExt = gl.getExtension("WEBKIT_WEBGL_depth_texture"); + + if(depthTextureExt) + { + let depthTexture = framebuffer.depthTexture; + + this.renderer.texture.bind(depthTexture, 0); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.TEXTURE_2D, + depthTexture._glTextures[this.CONTEXT_UID].texture, + 0); + } + } + + if(framebuffer.stencil || framebuffer.depth) + { + fbo.stencil = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, fbo.stencil); + + // TODO.. this is depth AND stencil? + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, fbo.stencil); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, framebuffer.width , framebuffer.height ); + //fbo.enableStencil(); + } + } + +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/GeometrySystem.js b/src/core/renderers/webgl/systems/GeometrySystem.js new file mode 100644 index 0000000..984e3bb --- /dev/null +++ b/src/core/renderers/webgl/systems/GeometrySystem.js @@ -0,0 +1,216 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; +import glCore from 'pixi-gl-core'; + + +const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class GeometrySystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this._activeVao = null; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + } + + /** + * Binds geometry so that is can be drawn. Creating a Vao if required + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + */ + bind(geometry, glShader) + { + const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + + this.bindVao(vao); + + // TODO - optimise later! + // don't need to loop through if nothing changed! + // maybe look to add an 'autoupdate' to geometry? + for (let i = 0; i < geometry.buffers.length; i++) + { + const buffer = geometry.buffers[i]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + if (buffer._updateID !== glBuffer._updateID) + { + glBuffer._updateID = buffer._updateID; + // TODO - partial upload?? + + glBuffer.upload(buffer.data, 0); + } + } + } + + /** + * Creates a Vao with the same structure as the geometry and stores it on the geometry. + * @private + * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for + * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. + * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. + */ + initGeometryVao(geometry, glShader) + { + const gl = this.gl; + + this.bindVao(null); + + const vao = this.createVao(); + + const buffers = geometry.buffers; + const attributes = geometry.attributes; + + // first update - and create the buffers! + for (let i = 0; i < buffers.length; i++) + { + const buffer = buffers[i]; + + if (!buffer._glBuffers[this.CONTEXT_UID]) + { + if (buffer.index) + { + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); + } + else + { + /* eslint-disable max-len */ + buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + } + } + } + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); + } + + const tempStride = {}; + const tempStart = {}; + + for (const j in buffers) + { + tempStride[j] = 0; + tempStart[j] = 0; + } + + for (const j in attributes) + { + tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + } + + for (const j in attributes) + { + const attribute = attributes[j]; + const glAttribute = glShader.attributes[j]; + + if (attribute.stride === undefined) + { + if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + { + attribute.stride = 0; + } + else + { + attribute.stride = tempStride[attribute.buffer]; + } + } + + if (attribute.start === undefined) + { + attribute.start = tempStart[attribute.buffer]; + + tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + } + } + + // next update the attributes buffer.. + for (const j in attributes) + { + const attribute = attributes[j]; + const buffer = buffers[attribute.buffer]; + + const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + + // need to know the shader as it means we can be lazy and let pixi do the work for us.. + // stride, start, type? + vao.addAttribute(glBuffer, + glShader.attributes[j], + attribute.type || 5126, // (5126 = FLOAT) + attribute.normalized, + attribute.stride, + attribute.start, + attribute.instance); + } + + geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + + return vao; + } + + draw(type, size, start, instanceCount) + { + this._activeVao.draw(type, size, start, instanceCount); + } + + /** + * Creates a new VAO from this renderer's context and state. + * + * @return {VertexArrayObject} The new VAO. + */ + createVao() + { + return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); + } + + /** + * Changes the current Vao to the one given in parameter + * + * @param {PIXI.VertexArrayObject} vao - the new Vao + * @return {PIXI.WebGLRenderer} Returns itself. + */ + bindVao(vao) + { + if (this._activeVao === vao) + { + return this; + } + + if (vao) + { + vao.bind(); + } + else if (this._activeVao) + { + // TODO this should always be true i think? + this._activeVao.unbind(); + } + + this._activeVao = vao; + + return this; + } +} diff --git a/src/core/renderers/webgl/systems/MaskSystem.js b/src/core/renderers/webgl/systems/MaskSystem.js new file mode 100644 index 0000000..1a55dad --- /dev/null +++ b/src/core/renderers/webgl/systems/MaskSystem.js @@ -0,0 +1,199 @@ +import WebGLSystem from './WebGLSystem'; +import AlphaMaskFilter from '../filters/spriteMask/SpriteMaskFilter'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class MaskSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + // TODO - we don't need both! + this.scissor = false; + this.scissorData = null; + this.scissorRenderTarget = null; + + this.enableScissor = true; + + this.alphaMaskPool = []; + this.alphaMaskIndex = 0; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushMask(target, maskData) + { + // TODO the root check means scissor rect will not + // be used on render textures more info here: + // https://github.com/pixijs/pixi.js/pull/3545 + + if (maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else if (this.enableScissor + && !this.scissor + && this.renderer._activeRenderTarget.root + && !this.renderer.stencil.stencilMaskStack.length + && maskData.isFastRect()) + { + const matrix = maskData.worldTransform; + + let rot = Math.atan2(matrix.b, matrix.a); + + // use the nearest degree! + rot = Math.round(rot * (180 / Math.PI)); + + if (rot % 90) + { + this.pushStencilMask(maskData); + } + else + { + this.pushScissorMask(target, maskData); + } + } + else + { + this.pushStencilMask(maskData); + } + } + + /** + * Removes the last mask from the mask stack and doesn't return it. + * + * @param {PIXI.DisplayObject} target - Display Object to pop the mask from + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + popMask(target, maskData) + { + if (maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else if (this.enableScissor && !this.renderer.stencil.stencilMaskStack.length) + { + this.popScissorMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.RenderTarget} target - Display Object to push the sprite mask to + * @param {PIXI.Sprite} maskData - Sprite to be used as the mask + */ + pushSpriteMask(target, maskData) + { + let alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex]; + + if (!alphaMaskFilter) + { + alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new AlphaMaskFilter(maskData)]; + } + + alphaMaskFilter[0].resolution = this.renderer.resolution; + alphaMaskFilter[0].maskSprite = maskData; + + // TODO - may cause issues! + target.filterArea = maskData.getBounds(true); + + this.renderer.filterSystem.pushFilter(target, alphaMaskFilter); + + this.alphaMaskIndex++; + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popSpriteMask() + { + this.renderer.filterSystem.popFilter(); + this.alphaMaskIndex--; + } + + /** + * Applies the Mask and adds it to the current filter stack. + * + * @param {PIXI.Sprite|PIXI.Graphics} maskData - The masking data. + */ + pushStencilMask(maskData) + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.pushStencil(maskData); + } + + /** + * Removes the last filter from the filter stack and doesn't return it. + * + */ + popStencilMask() + { + this.renderer.currentRenderer.stop(); + this.renderer.stencil.popStencil(); + } + + /** + * + * @param {PIXI.DisplayObject} target - Display Object to push the mask to + * @param {PIXI.Graphics} maskData - The masking data. + */ + pushScissorMask(target, maskData) + { + maskData.renderable = true; + + const renderTarget = this.renderer._activeRenderTarget; + + const bounds = maskData.getBounds(); + + bounds.fit(renderTarget.size); + maskData.renderable = false; + + this.renderer.gl.enable(this.renderer.gl.SCISSOR_TEST); + + const resolution = this.renderer.resolution; + + this.renderer.gl.scissor( + bounds.x * resolution, + (renderTarget.root ? renderTarget.size.height - bounds.y - bounds.height : bounds.y) * resolution, + bounds.width * resolution, + bounds.height * resolution + ); + + this.scissorRenderTarget = renderTarget; + this.scissorData = maskData; + this.scissor = true; + } + + /** + * + * + */ + popScissorMask() + { + this.scissorRenderTarget = null; + this.scissorData = null; + this.scissor = false; + + // must be scissor! + const gl = this.renderer.gl; + + gl.disable(gl.SCISSOR_TEST); + } +} diff --git a/src/core/renderers/webgl/systems/NewTextureSystem.js b/src/core/renderers/webgl/systems/NewTextureSystem.js new file mode 100644 index 0000000..49e1820 --- /dev/null +++ b/src/core/renderers/webgl/systems/NewTextureSystem.js @@ -0,0 +1,244 @@ +import WebGLSystem from './WebGLSystem'; +import { GLFramebuffer, GLTexture } from 'pixi-gl-core'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class TextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.boundTextures = [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ]; + + this.currentLocation = -1; + } + + /** + * Sets up the renderer context and necessary buffers. + * + * @private + */ + contextChange() + { + const gl = this.gl = this.renderer.gl; + this.CONTEXT_UID = this.renderer.CONTEXT_UID; + + // TODO move this.. to a nice make empty textures class.. + this.emptyTextures = {} + + this.emptyTextures[gl.TEXTURE_2D] = new GLTexture.fromData(this.gl, null, 1, 1); + this.emptyTextures[gl.TEXTURE_CUBE_MAP] = new GLTexture(this.gl); + + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.emptyTextures[gl.TEXTURE_CUBE_MAP].texture); + + for (var i = 0; i < 6; i++) + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + for (var i = 0; i < this.boundTextures.length; i++) { + this.bind(null, i); + } + } + + bind(texture, location) + { + + const gl = this.gl; + + + location = location || 0; + + if(this.currentLocation !== location) + { + this.currentLocation = location; + gl.activeTexture(gl.TEXTURE0 + location); + } + + if(texture) + { + texture = texture.baseTexture || texture; + + if(texture.valid) + { + + const glTexture = texture._glTextures[this.CONTEXT_UID] || this.initTexture(texture); + + gl.bindTexture(texture.target, glTexture.texture); + + if(glTexture.dirtyId !== texture.dirtyId) + { + glTexture.dirtyId = texture.dirtyId; + this.updateTexture(texture); + } + + this.boundTextures[location] = texture; + } + } + else + { + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[gl.TEXTURE_2D].texture); + this.boundTextures[location] = null; + } + } + + unbind(texture) + { + const gl = this.gl; + + for (var i = 0; i < this.boundTextures.length; i++) { + + if(this.boundTextures[i] === texture) + { + if(this.currentLocation !== i) + { + gl.activeTexture(gl.TEXTURE0 + i); + this.currentLocation = i; + } + + gl.bindTexture(gl.TEXTURE_2D, this.emptyTextures[texture.target].texture); + this.boundTextures[i] = null; + } + } + } + + initTexture(texture) + { + const gl = this.gl; + + var glTexture = new GLTexture(this.gl, -1, -1, texture.format, texture.type); + glTexture.premultiplyAlpha = texture.premultiplyAlpha; + // guarentee an update.. + glTexture.dirtyId = -1; + + texture._glTextures[this.CONTEXT_UID] = glTexture; + + return glTexture; + } + + updateTexture(texture) + { + const glTexture = texture._glTextures[this.CONTEXT_UID]; + const gl = this.gl; + //console.log(gl); + + // TODO there are only 3 textures as far as im aware? + // Cube / 2D and later 3d. (the latter is WebGL2, we will get to that soon!) + if(texture.target === gl.TEXTURE_CUBE_MAP) + { + // console.log( gl.UNSIGNED_BYTE) + for (var i = 0; i < texture.sides.length; i++) + { + // TODO - we should only upload what changed.. + // but im sure this is not going to be a problem just yet! + var texturePart = texture.sides[i]; + + if(texturePart.resource) + { + if(texturePart.resource.uploadable) + { + + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.format, + texture.type, + texturePart.resource.source); + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + texturePart.resource.source); + } + } + else + { + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + texturePart.side, + 0, + texture.format, + texture.width, + texture.height, + 0, + texture.format, + texture.type, + null); + } + } + } + else + { + if(texture.resource) + { + if(texture.resource.uploadable) + { + glTexture.upload(texture.resource.source); + + } + else + { + glTexture.uploadData(texture.resource.source, texture.width, texture.height); + } + } + else + { + glTexture.uploadData(null, texture.width, texture.height); + } + } + + // lets only update what changes.. + this.setStyle(texture); + } + + setStyle(texture) + { + const gl = this.gl; + + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_S, texture.wrapMode); + gl.texParameteri(texture.target, gl.TEXTURE_WRAP_T, texture.wrapMode); + + if(texture.mipmap) + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); + } + else + { + gl.texParameteri(texture.target, gl.TEXTURE_MIN_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } + + gl.texParameteri(texture.target, gl.TEXTURE_MAG_FILTER, texture.scaleMode ? gl.LINEAR : gl.NEAREST); + } +} \ No newline at end of file diff --git a/src/core/renderers/webgl/systems/ProjectionSystem.js b/src/core/renderers/webgl/systems/ProjectionSystem.js new file mode 100644 index 0000000..33fe65e --- /dev/null +++ b/src/core/renderers/webgl/systems/ProjectionSystem.js @@ -0,0 +1,104 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class ProjectionSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.projectionMatrix = new Matrix(); + } + + update(destinationFrame, sourceFrame, root) + { + this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + + this.calculateProjection(this.destinationFrame, this.sourceFrame, root); + + this.renderer.globalUniforms.uniforms.projectionMatrix = this.projectionMatrix; + this.renderer.globalUniforms.update(); + + const gl = this.renderer.gl; + + // TODO this is bot needed here? + const resolution = 1; + + // TODO add a check as them may be the same! + if (this.destinationFrame !== this.sourceFrame) + { +// gl.enable(gl.SCISSOR_TEST); + gl.scissor( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + else + { + // gl.disable(gl.SCISSOR_TEST); + } + + // TODO - does not need to be updated all the time?? + gl.viewport( + this.destinationFrame.x | 0, + this.destinationFrame.y | 0, + (this.destinationFrame.width * resolution) | 0, + (this.destinationFrame.height * resolution) | 0 + ); + } + + /** + * Updates the projection matrix based on a projection frame (which is a rectangle) + * + * @param {Rectangle} destinationFrame - The destination frame. + * @param {Rectangle} sourceFrame - The source frame. + */ + calculateProjection(destinationFrame, sourceFrame, root) + { + const pm = this.projectionMatrix; + + pm.identity(); + + // TODO: make dest scale source + if (!root) + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = 1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = -1 - (sourceFrame.y * pm.d); + } + else + { + pm.a = 1 / destinationFrame.width * 2; + pm.d = -1 / destinationFrame.height * 2; + + pm.tx = -1 - (sourceFrame.x * pm.a); + pm.ty = 1 - (sourceFrame.y * pm.d); + } + } + + + /** + * Sets the transform of the active render target to the given matrix + * + * @param {PIXI.Matrix} matrix - The transformation matrix + */ + setTransform(matrix) + { + // this._activeRenderTarget.transform = matrix; + } + +} diff --git a/src/core/renderers/webgl/systems/RenderTextureSystem.js b/src/core/renderers/webgl/systems/RenderTextureSystem.js new file mode 100644 index 0000000..d4a7d70 --- /dev/null +++ b/src/core/renderers/webgl/systems/RenderTextureSystem.js @@ -0,0 +1,79 @@ +import WebGLSystem from './WebGLSystem'; +import { Rectangle, Matrix } from '../../../math'; + +const tempRect = new Rectangle(); + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ + +export default class RenderTextureSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.clearColor = renderer._backgroundColorRgba; + } + + bind(renderTexture) + { + // TODO - do we want this?? + if(this.renderTexture === renderTexture)return; + + this.renderTexture = renderTexture; + + if(renderTexture) + { + this.renderer.framebuffer.bind(renderTexture.baseTexture.frameBuffer); + this.renderer.projection.update(renderTexture.frame, renderTexture.frame, false); + } + else + { + this.renderer.framebuffer.bind(null); + + tempRect.width = this.renderer.width; + tempRect.height = this.renderer.height; + // TODO store this.. + this.renderer.projection.update(tempRect, tempRect, true); + } + + const glShader = this.renderer.shader.getGLShader() + + if (glShader) + { + // glShader.uniforms.projectionMatrix = this.renderer.projection.projectionMatrix.toArray(true); + } + } + + /** + * Erases the render texture and fills the drawing area with a colour + * + * @param {number} [clearColor] - The colour + * @return {PIXI.WebGLRenderer} Returns itself. + */ + clear(clearColor) + { + if(this.renderTexture) + { + clearColor = clearColor || this.renderTexture.baseTexture.clearColor; + } + else + { + clearColor = clearColor || this.clearColor; + } + + this.renderer.framebuffer.clear(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + + resize(screenWidth, screenHeight) + { + // resize the root only! + this.bind(null) + } +} diff --git a/src/core/renderers/webgl/systems/ShaderSystem.js b/src/core/renderers/webgl/systems/ShaderSystem.js new file mode 100644 index 0000000..6844bd2 --- /dev/null +++ b/src/core/renderers/webgl/systems/ShaderSystem.js @@ -0,0 +1,165 @@ +import WebGLSystem from './WebGLSystem'; +import { GLShader } from 'pixi-gl-core'; +import { PRECISION } from '../../../const'; +import generateUniformsSync from '../../../shader/generateUniformsSync2'; + +let UID = 0; + +/** + * Helper class to create a webGL Texture + * + * @class + * @memberof PIXI + */ +export default class ShaderSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - A reference to the current renderer + */ + constructor(renderer) + { + + super(renderer); + + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = null; + + this.shader = null; + + this.id = UID++; + } + + contextChange(gl) + { + this.gl = gl; + } + + /** + * Changes the current shader to the one given in parameter + * + * @param {PIXI.Shader} shader - the new shader + * @param {boolean} dontSync - false if the shader should automatically sync its uniforms. + * @returns {PIXI.glCore.GLShader} the glShader that belongs to the shader. + */ + bind(shader, dontSync) + { + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + + // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. + // if (this.shader !== shader) + // { + if (this.shader !== shader) + { + this.shader = shader; + glShader.bind(); + } + + if (!dontSync) + { + this.syncUniformGroup(shader.uniformGroup); + } + + return glShader; + } + + /** + * Uploads the uniforms values to the currently bound shader. + * + * @param {object} uniforms - the uniforms valiues that be applied to the current shader + */ + setUniforms(uniforms) + { + const shader = this.shader.program; + const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; + + shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); + } + + setUniformsGroups(uniformGroups) + { + + const glShader = this.getGLShader(); + + const group = uniformGroups[0]; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + + } + + syncUniformGroup(group) + { + const glShader = this.getGLShader(); + + if(!group.static || group.dirtyId !== glShader.uniformGroups[group.id]) + { + glShader.uniformGroups[group.id] = group.dirtyId; + const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSynGroups(group); + + syncFunc(glShader.uniformData, group.uniforms, this.renderer); + } + + } + + createSynGroups(group) + { + group.syncUniforms[this.shader.program.id] = generateUniformsSync(group, this.shader.program.uniformData); + + return group.syncUniforms[this.shader.program.id]; + } + /** + * Returns the underlying GLShade rof the currently bound shader. + * This can be handy for when you to have a little more control over the setting of your uniforms. + * + * @return {PIXI.glCore.Shader} the glShader for the currently bound Shader for this context + */ + getGLShader() + { + if(this.shader) + { + return this.shader.program.glShaders[this.renderer.CONTEXT_UID]; + } + else + { + return null; + } + } + + /** + * Generates a GLShader verion of the Shader provided. + * + * @private + * @param {PIXI.Shader} shader the shader that the glShader will be based on. + * @return {PIXI.glCore.GLShader} A shiney new GLShader + */ + generateShader(shader) + { + const program = shader.program; + const attribMap = {}; + + for (const i in program.attributeData) + { + attribMap[i] = program.attributeData[i].location; + } + + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); + + program.glShaders[this.renderer.CONTEXT_UID] = glShader; + + return glShader; + } + + /** + * Destroys this System and removes all its textures + */ + destroy() + { + // TODO implement destroy method for ShaderSystem + this.destroyed = true; + } +} diff --git a/src/core/renderers/webgl/systems/StateSystem.js b/src/core/renderers/webgl/systems/StateSystem.js new file mode 100755 index 0000000..be6c69b --- /dev/null +++ b/src/core/renderers/webgl/systems/StateSystem.js @@ -0,0 +1,289 @@ +import mapWebGLBlendModesToPixi from '../utils/mapWebGLBlendModesToPixi'; +import WebGLSystem from './WebGLSystem'; +import WebGLState from '../State'; + +const BLEND = 0; +const OFFSET = 1; +const CULLING = 2; +const DEPTH_TEST = 3; +const WINDING = 4; + +/** + * A WebGL state machines + * + * @memberof PIXI + * @class + */ +export default class StateSystem extends WebGLSystem +{ + /** + * @param {WebGLRenderingContext} gl - The current WebGL rendering context + */ + constructor(renderer) + { + super(renderer); + + this.gl = null; + + this.maxAttribs = null; + + // check we have vao.. + this.nativeVaoExtension = null; + + this.attribState = null; + + this.stateId = 0; + this.polygonOffset = 0; + this.blendMode = 17; + + this.map = []; + + // map functions for when we set state.. + this.map[BLEND] = this.setBlend; + this.map[OFFSET] = this.setOffset; + this.map[CULLING] = this.setCullFace; + this.map[DEPTH_TEST] = this.setDepthTest; + this.map[WINDING] = this.setFrontFace; + + this.checks = []; + + this.defaultState = new WebGLState(); + this.defaultState.blend = true; + this.defaultState.depth = true; + + + } + + contextChange(gl) + { + /** + * The current WebGL rendering context + * + * @member {WebGLRenderingContext} + */ + this.gl = gl; + + this.maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + // check we have vao.. + this.nativeVaoExtension = ( + gl.getExtension('OES_vertex_array_object') + || gl.getExtension('MOZ_OES_vertex_array_object') + || gl.getExtension('WEBKIT_OES_vertex_array_object') + ); + + this.attribState = { + tempAttribState: new Array(this.maxAttribs), + attribState: new Array(this.maxAttribs), + }; + + this.blendModes = mapWebGLBlendModesToPixi(gl); + + this.setState(this.defaultState); + + this.reset(); + } + + /** + * Sets the current state + * + * @param {*} state - The state to set. + */ + setState(state) + { + state = state || this.defaultState; + + // TODO maybe to an object check? ( this.state === state )? + if (this.stateId !== state.data) + { + let diff = this.stateId ^ state.data; + let i = 0; + + // order from least to most common + while (diff) + { + if (diff & 1) + { + // state change! + this.map[i].call(this, !!(state.data & (1 << i))); + } + + diff = diff >> 1; + i++; + } + + this.stateId = state.data; + } + + // based on the above settings we check for specific modes.. + // for example if blend is active we check and set the blend modes + // or of polygon offset is active we check the poly depth. + for (let i = 0; i < this.checks.length; i++) + { + this.checks[i](this, state); + } + } + + /** + * Enables or disabled blending. + * + * @param {boolean} value - Turn on or off webgl blending. + */ + setBlend(value) + { + this.updateCheck(StateSystem.checkBlendMode, value); + + this.gl[value ? 'enable' : 'disable'](this.gl.BLEND); + } + + /** + * Enables or disable polygon offset fill + * + * @param {boolean} value - Turn on or off webgl polygon offset testing. + */ + setOffset(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.POLYGON_OFFSET_FILL); + } + + /** + * Sets whether to enable or disable depth test. + * + * @param {boolean} value - Turn on or off webgl depth testing. + */ + setDepthTest(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.DEPTH_TEST); + } + + /** + * Sets whether to enable or disable cull face. + * + * @param {boolean} value - Turn on or off webgl cull face. + */ + setCullFace(value) + { + this.gl[value ? 'enable' : 'disable'](this.gl.CULL_FACE); + } + + /** + * Sets the gl front face. + * + * @param {boolean} value - true is clockwise and false is counter-clockwise + */ + setFrontFace(value) + { + this.gl.frontFace(this.gl[value ? 'CW' : 'CCW']); + } + + /** + * Sets the blend mode. + * + * @param {number} value - The blend mode to set to. + */ + setBlendMode(value) + { + if (value === this.blendMode) + { + return; + } + + this.blendMode = value; + this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + } + + /** + * Sets the polygon offset. + * + * @param {number} value - the polygon offset + * @param {number} scale - the polygon offset scale + */ + setPolygonOffset(value, scale) + { + this.gl.polygonOffset(value, scale); + } + + /** + * Disables all the vaos in use + * + */ + resetAttributes() + { + for (let i = 0; i < this.attribState.tempAttribState.length; i++) + { + this.attribState.tempAttribState[i] = 0; + } + + for (let i = 0; i < this.attribState.attribState.length; i++) + { + this.attribState.attribState[i] = 0; + } + + // im going to assume one is always active for performance reasons. + for (let i = 1; i < this.maxAttribs; i++) + { + this.gl.disableVertexAttribArray(i); + } + } + + // used + /** + * Resets all the logic and disables the vaos + */ + reset() + { + // unbind any VAO if they exist.. + if (this.nativeVaoExtension) + { + this.nativeVaoExtension.bindVertexArrayOES(null); + } + + // reset all attributes.. + this.resetAttributes(); + + this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false); + + this.setBlendMode(0); + + // TODO? + // this.setState(this.defaultState); + } + + /** + * checks to see which updates should be checked based on which settings have been activated + * for example if blend is enabled then we shold check the blend modes each time the state is changed + * or if poygon fill is activated then we need to check if the polygone offset changes. + * The idea is that we only check what we have too. + * + * @param {Function} func the checking function to add or remove + * @param {boolean} value should the check function be added or removed. + */ + updateCheck(func, value) + { + const index = this.checks.indexOf(func); + + if (value && index === -1) + { + this.checks.push(func); + } + else if (!value && index !== -1) + { + this.checks.splice(index, 1); + } + } + + /** + * A private little wrapper function that we call to check the blend mode. + * + * @static + * @private + * @param {PIXI.StateSystem} System the System to perform the state check on + * @param {PIXI.State} state the state that the blendMode will pulled from + */ + static checkBlendMode(system, state) + { + system.setBlendMode(state.blendMode); + } + + // TODO - add polygon offset? +} diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js new file mode 100644 index 0000000..3ecd92d --- /dev/null +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -0,0 +1,113 @@ +import WebGLSystem from './WebGLSystem'; + +/** + * @class + * @extends PIXI.WebGLSystem + * @memberof PIXI + */ +export default class StencilSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + this.stencilMaskStack = null; + } + + /** + * Changes the mask stack that is used by this System. + * + * @param {PIXI.Graphics[]} stencilMaskStack - The mask stack + */ + setMaskStack(stencilMaskStack) + { + this.stencilMaskStack = stencilMaskStack; + + const gl = this.renderer.gl; + + if (stencilMaskStack.length === 0) + { + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.enable(gl.STENCIL_TEST); + } + } + + /** + * Applies the Mask and adds it to the current filter stack. @alvin + * + * @param {PIXI.Graphics} graphics - The mask + */ + pushStencil(graphics) + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + this.renderer._activeRenderTarget.attachStencilBuffer(); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + if (sms.length === 0) + { + gl.enable(gl.STENCIL_TEST); + gl.clear(gl.STENCIL_BUFFER_BIT); + gl.stencilFunc(gl.ALWAYS, 1, 1); + } + + sms.push(graphics); + + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + + /** + * TODO @alvin + */ + popStencil() + { + this.renderer.setObjectRenderer(this.renderer.plugins.graphics); + + const gl = this.renderer.gl; + const sms = this.stencilMaskStack; + + const graphics = sms.pop(); + + if (sms.length === 0) + { + // the stack is empty! + gl.disable(gl.STENCIL_TEST); + } + else + { + gl.colorMask(false, false, false, false); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.DECR); + + this.renderer.plugins.graphics.render(graphics); + + gl.colorMask(true, true, true, true); + gl.stencilFunc(gl.NOTEQUAL, 0, sms.length); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + } + } + + /** + * Destroys the mask stack. + * + */ + destroy() + { + super.destroy(this); + + this.stencilMaskStack.stencilStack = null; + } +} diff --git a/src/core/renderers/webgl/systems/TextureGCSystem.js b/src/core/renderers/webgl/systems/TextureGCSystem.js new file mode 100644 index 0000000..cd9f24b --- /dev/null +++ b/src/core/renderers/webgl/systems/TextureGCSystem.js @@ -0,0 +1,111 @@ +import WebGLSystem from './WebGLSystem'; +import { GC_MODES } from '../../../const'; +import settings from '../../../settings'; + +/** + * TextureGarbageCollector. This class manages the GPU and ensures that it does not get clogged + * up with textures that are no longer being used. + * + * @class + * @memberof PIXI + */ +export default class TextureGCSystem extends WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. + */ + constructor(renderer) + { + super(renderer); + + this.count = 0; + this.checkCount = 0; + this.maxIdle = settings.GC_MAX_IDLE; + this.checkCountMax = settings.GC_MAX_CHECK_COUNT; + this.mode = settings.GC_MODE; + } + + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + postrender() + { + this.count++; + + if (this.mode === GC_MODES.MANUAL) + { + return; + } + + this.checkCount++; + + if (this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } + } + + /** + * Checks to see when the last time a texture was used + * if the texture has not been used for a specified amount of time it will be removed from the GPU + */ + run() + { + const tm = this.renderer.texture; + const managedTextures = tm._managedTextures; + let wasRemoved = false; + + for (let i = 0; i < managedTextures.length; i++) + { + const texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + let j = 0; + + for (let i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } + } + + /** + * Removes all the textures within the specified displayObject and its children from the GPU + * + * @param {PIXI.DisplayObject} displayObject - the displayObject to remove the textures from. + */ + unload(displayObject) + { + const tm = this.renderer.textureSystem; + + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) + { + tm.destroyTexture(displayObject._texture, true); + } + + for (let i = displayObject.children.length - 1; i >= 0; i--) + { + this.unload(displayObject.children[i]); + } + } +} diff --git a/src/core/renderers/webgl/systems/WebGLSystem.js b/src/core/renderers/webgl/systems/WebGLSystem.js new file mode 100644 index 0000000..1fe53a7 --- /dev/null +++ b/src/core/renderers/webgl/systems/WebGLSystem.js @@ -0,0 +1,40 @@ +/** + * @class + * @memberof PIXI + */ +export default class WebGLSystem +{ + /** + * @param {PIXI.WebGLRenderer} renderer - The renderer this manager works for. + */ + constructor(renderer) + { + /** + * The renderer this manager works for. + * + * @member {PIXI.WebGLRenderer} + */ + this.renderer = renderer; + + this.renderer.runners.contextChange.add(this); + } + + /** + * Generic method called when there is a WebGL context change. + * + */ + contextChange() + { + // do some codes init! + } + + /** + * Generic destroy methods to be overridden by the subclass + * + */ + destroy() + { + this.renderer.runners.contextChange.remove(this); + this.renderer = null; + } +} diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index 41732be..f67a273 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,13 +1,13 @@ -import WebGLManager from '../managers/WebGLManager'; +import WebGLSystem from '../systems/WebGLSystem'; /** * Base for a common object renderer that can be used as a system renderer plugin. * * @class - * @extends PIXI.WebGLManager + * @extends PIXI.WebGLSystem * @memberof PIXI */ -export default class ObjectRenderer extends WebGLManager +export default class ObjectRenderer extends WebGLSystem { /** * Starts the renderer and sets the shader