diff --git a/src/renderer/webgl/wrappers/WebGLFramebufferWrapper.js b/src/renderer/webgl/wrappers/WebGLFramebufferWrapper.js new file mode 100644 index 000000000..bb732b868 --- /dev/null +++ b/src/renderer/webgl/wrappers/WebGLFramebufferWrapper.js @@ -0,0 +1,184 @@ +var Class = require('../../../utils/Class'); + +/** + * Possible errors that can be thrown by `gl.checkFramebufferStatus()`. + */ +var errors = { + 36054: 'Incomplete Attachment', + 36055: 'Missing Attachment', + 36057: 'Incomplete Dimensions', + 36061: 'Framebuffer Unsupported' +}; + +/** + * @classdesc + * Wrapper for a WebGL frame buffer, + * containing all the information that was used to create it. + * + * A WebGLFramebuffer should never be exposed outside the WebGLRenderer, + * so the WebGLRenderer can handle context loss and other events + * without other systems having to be aware of it. + * Always use WebGLFramebufferWrapper instead. + * + * @method Phaser.Renderer.WebGL.WebGLRenderer#createFramebuffer + * @since 3.80.0 + * + * @param {WebGLRenderingContext} gl - The WebGLRenderingContext to create the WebGLFramebuffer for. + * @param {number} width - If `addDepthStencilBuffer` is true, this controls the width of the depth stencil. + * @param {number} height - If `addDepthStencilBuffer` is true, this controls the height of the depth stencil. + * @param {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} renderTexture - The color texture where the color pixels are written. + * @param {boolean} [addDepthStencilBuffer=false] - Create a Renderbuffer for the depth stencil? + */ +var WebGLFramebufferWrapper = new Class({ + initialize: function WebGLFramebufferWrapper (renderer, width, height, renderTexture, addDepthStencilBuffer) + { + /** + * The WebGLFramebuffer being wrapped by this class. + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#webGLFramebuffer + * @type {?WebGLFramebuffer} + * @default null + * @since 3.80.0 + */ + this.webGLFramebuffer = null; + + /** + * The WebGLRenderer instance that owns this WebGLFramebufferWrapper. + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#renderer + * @type {Phaser.Renderer.WebGL.WebGLRenderer} + * @since 3.80.0 + */ + this.renderer = renderer; + + /** + * Width of the depth stencil. + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#width + * @type {number} + * @since 3.80.0 + */ + this.width = width; + + /** + * Height of the depth stencil. + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#height + * @type {number} + * @since 3.80.0 + */ + this.height = height; + + /** + * The color texture where the color pixels are written. + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#renderTexture + * @type {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} + * @since 3.80.0 + */ + this.renderTexture = renderTexture; + + /** + * Create a Renderbuffer for the depth stencil? + * + * @name Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#addDepthStencilBuffer + * @type {boolean} + * @default false + * @since 3.80.0 + */ + this.addDepthStencilBuffer = !!addDepthStencilBuffer; + + this.createResource(); + }, + + /** + * Creates a WebGLFramebuffer from the given parameters. + * + * This is called automatically by the constructor. It may also be + * called again if the WebGLFramebuffer needs re-creating. + * + * @method Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#createResource + * @since 3.80.0 + */ + createResource: function () + { + var renderer = this.renderer; + var gl = renderer.gl; + var renderTexture = this.renderTexture; + var complete = 0; + var framebuffer = gl.createFramebuffer(); + + // This property must exist before calling `setFramebuffer`. + this.webGLFramebuffer = framebuffer; + + renderer.setFramebuffer(this); + + renderTexture.isRenderTexture = true; + renderTexture.isAlphaPremultiplied = false; + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture.webGLTexture, 0); + + complete = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + + if (complete !== gl.FRAMEBUFFER_COMPLETE) + { + throw new Error('Framebuffer status: ' + (errors[complete] || complete)); + } + + if (this.addDepthStencilBuffer) + { + var depthStencilBuffer = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, this.width, this.height); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer); + } + + renderer.setFramebuffer(null); + }, + + /** + * Destroys this WebGLFramebufferWrapper. + * + * @method Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper#destroy + * @since 3.80.0 + */ + destroy: function () + { + if (this.webGLFramebuffer === null) + { + return; + } + var gl = this.renderer.gl; + + gl.bindFramebuffer(gl.FRAMEBUFFER, this.webGLFramebuffer); + + this.renderTexture = null; + + // Check for a color attachment and remove it + var colorAttachment = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + + if (colorAttachment !== null) + { + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); + + gl.deleteTexture(colorAttachment); + } + + // Check for a depth-stencil attachment and remove it + var depthStencilAttachment = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); + + if (depthStencilAttachment !== null) + { + gl.deleteRenderbuffer(depthStencilAttachment); + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.deleteFramebuffer(this.webGLFramebuffer); + + this.webGLFramebuffer = null; + } +}); + +module.exports = WebGLFramebufferWrapper;