├── .gitignore ├── .npmignore ├── index.js ├── package.json ├── LICENSE.md ├── README.md ├── test.js └── state.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | bundle.js 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | bundle.js 5 | test 6 | test.js 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var stateReset = require('./state') 2 | 3 | module.exports = Reset 4 | module.exports.state = stateReset 5 | 6 | function Reset(gl) { 7 | var cleanup = [ 8 | 'Buffer' 9 | , 'Framebuffer' 10 | , 'Renderbuffer' 11 | , 'Program' 12 | , 'Shader' 13 | , 'Texture' 14 | ].map(function(suffix) { 15 | var remove = 'delete' + suffix 16 | var create = 'create' + suffix 17 | var original = gl[create] 18 | var handles = [] 19 | 20 | gl[create] = function() { 21 | var handle = original.apply(this, arguments) 22 | handles.push(handle) 23 | return handle 24 | } 25 | 26 | return { 27 | remove: remove 28 | , handles: handles 29 | } 30 | }) 31 | 32 | return function reset() { 33 | cleanup.forEach(function(kind) { 34 | for (var i = 0; i < kind.handles.length; i++) { 35 | gl[kind.remove].call(gl, kind.handles[i]) 36 | } 37 | }) 38 | 39 | stateReset(gl) 40 | 41 | return gl 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gl-reset", 3 | "version": "1.0.0", 4 | "description": "Completely reset the state of a WebGL context, deleting any allocated resources", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "browserify test.js | smokestack --tape | tap-spec" 9 | }, 10 | "author": { 11 | "name": "Hugh Kennedy", 12 | "email": "hughskennedy@gmail.com", 13 | "url": "http://hughsk.io/" 14 | }, 15 | "dependencies": {}, 16 | "devDependencies": { 17 | "browserify": "^6.2.0", 18 | "smokestack": "^2.0.0", 19 | "tap-spec": "^1.0.1", 20 | "tape": "^3.0.1" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/stackgl/gl-reset.git" 25 | }, 26 | "keywords": [ 27 | "webgl", 28 | "stackgl", 29 | "gl", 30 | "reset", 31 | "state", 32 | "remove", 33 | "delete", 34 | "purge" 35 | ], 36 | "homepage": "https://github.com/stackgl/gl-reset", 37 | "bugs": { 38 | "url": "https://github.com/stackgl/gl-reset/issues" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2014 [stackgl](http://github.com/stackgl/) contributors 5 | 6 | *stackgl contributors listed at * 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gl-reset 2 | ![](http://img.shields.io/badge/stability-experimental-orange.svg?style=flat) 3 | ![](http://img.shields.io/npm/v/gl-reset.svg?style=flat) 4 | ![](http://img.shields.io/npm/dm/gl-reset.svg?style=flat) 5 | ![](http://img.shields.io/npm/l/gl-reset.svg?style=flat) 6 | 7 | Completely reset the state of a WebGL context, deleting any allocated resources. 8 | 9 | Resetting is slow and instrumentation introduces a very slight overhead on 10 | creating new resources. However, this is useful: 11 | 12 | * If you want to safely recycle a WebGL context without state leaking between renders. 13 | * To capture and remove any generated resources during a render. 14 | 15 | There's a limit on the number of contexts you can have running 16 | simultaneously on a page, and (at least on Chrome) when hitting the threshold 17 | you simply lose the least recently created one. To work around this limit, 18 | you can instead reset and reuse a single WebGL context between multiple renders. 19 | 20 | ## Usage 21 | 22 | [![NPM](https://nodei.co/npm/gl-reset.png)](https://nodei.co/npm/gl-reset/) 23 | 24 | ### `reset = require('gl-reset')(gl)` 25 | 26 | Returns a function that, when called, will remove any existing resources and 27 | reset the current WebGL state. 28 | 29 | **Note:** this function must be created before creating any resources, as it 30 | instruments several WebGL methods to do its thing. 31 | 32 | ``` javascript 33 | var reset = require('gl-reset')(gl) 34 | 35 | function render() { 36 | // Triggers a context reset 37 | reset() 38 | } 39 | ``` 40 | 41 | ### `require('gl-reset/state')(gl)` 42 | 43 | Resets the state, without instrumenting the context and without removing any 44 | allocated resources. 45 | 46 | ## License 47 | 48 | MIT. See [LICENSE.md](http://github.com/hughsk/gl-reset/blob/master/LICENSE.md) for details. 49 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | var gl = document.body.appendChild(document.createElement('canvas')).getContext('webgl') 4 | var reset = require('./')(gl) 5 | 6 | var program = gl.createProgram() 7 | 8 | test('shaders', function(t) { 9 | var shader = gl.createShader(gl.VERTEX_SHADER) 10 | var src = 'precision mediump float; void main() { gl_Position = vec4(1); }' 11 | 12 | gl.shaderSource(shader, src) 13 | t.ok(gl.getShaderSource(shader), 'shader source initially present') 14 | 15 | reset() 16 | t.ok(!gl.getShaderSource(shader), 'shader source unavailable after reset') 17 | t.end() 18 | }) 19 | 20 | test('buffers', function(t) { 21 | var buffer = gl.createBuffer() 22 | 23 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer) 24 | t.ok(gl.getParameter(gl.ARRAY_BUFFER_BINDING) === buffer, 'buffer is bound') 25 | 26 | reset() 27 | t.ok(gl.getParameter(gl.ARRAY_BUFFER_BINDING) !== buffer, 'buffer is unbound on reset') 28 | t.end() 29 | }) 30 | 31 | test('programs', function(t) { 32 | var program = gl.createProgram() 33 | var vert = gl.createShader(gl.VERTEX_SHADER) 34 | var frag = gl.createShader(gl.FRAGMENT_SHADER) 35 | 36 | gl.shaderSource(vert, 'precision mediump float; uniform float a; void main() { gl_Position = vec4(a); }') 37 | gl.shaderSource(frag, 'precision mediump float; uniform float a; void main() { gl_FragColor = vec4(a); }') 38 | gl.compileShader(vert) 39 | gl.compileShader(frag) 40 | 41 | gl.attachShader(program, vert) 42 | gl.attachShader(program, frag) 43 | gl.linkProgram(program) 44 | gl.useProgram(program) 45 | 46 | t.ok(gl.getParameter(gl.CURRENT_PROGRAM) === program, 'program is bound') 47 | t.ok(gl.getUniformLocation(program, 'a'), 'uniform location available initially') 48 | reset() 49 | t.ok(gl.getParameter(gl.CURRENT_PROGRAM) !== program, 'program is unbound on reset') 50 | t.ok(!gl.getUniformLocation(program, 'a'), 'uniform location unavailable on reset') 51 | t.end() 52 | }) 53 | 54 | test('framebuffers', function(t) { 55 | var framebuffer = gl.createFramebuffer() 56 | 57 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer) 58 | t.ok(gl.getParameter(gl.FRAMEBUFFER_BINDING) === framebuffer, 'framebuffer is bound') 59 | reset() 60 | t.ok(gl.getParameter(gl.FRAMEBUFFER_BINDING) !== framebuffer, 'framebuffer is unbound on reset') 61 | t.end() 62 | }) 63 | 64 | test('renderbuffers', function(t) { 65 | var renderbuffer = gl.createRenderbuffer() 66 | 67 | gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer) 68 | t.ok(gl.getParameter(gl.RENDERBUFFER_BINDING) === renderbuffer, 'renderbuffer is bound') 69 | reset() 70 | t.ok(gl.getParameter(gl.RENDERBUFFER_BINDING) !== renderbuffer, 'renderbuffer is unbound on reset') 71 | t.end() 72 | }) 73 | 74 | test('textures', function(t) { 75 | var texture = gl.createTexture() 76 | 77 | gl.bindTexture(gl.TEXTURE_2D, texture) 78 | t.ok(gl.getParameter(gl.TEXTURE_BINDING_2D) === texture, 'texture is bound') 79 | reset() 80 | t.ok(gl.getParameter(gl.TEXTURE_BINDING_2D) !== texture, 'texture is unbound on reset') 81 | t.end() 82 | }) 83 | 84 | test('shutdown', function(t) { 85 | t.end() 86 | setTimeout(function() { 87 | window.close() 88 | }) 89 | }) 90 | -------------------------------------------------------------------------------- /state.js: -------------------------------------------------------------------------------- 1 | // 2 | // The code that follows was originally sourced from: 3 | // https://www.khronos.org/registry/webgl/sdk/debug/webgl-debug.js 4 | // 5 | 6 | module.exports = stateReset 7 | 8 | /* 9 | ** Copyright (c) 2012 The Khronos Group Inc. 10 | ** 11 | ** Permission is hereby granted, free of charge, to any person obtaining a 12 | ** copy of this software and/or associated documentation files (the 13 | ** "Materials"), to deal in the Materials without restriction, including 14 | ** without limitation the rights to use, copy, modify, merge, publish, 15 | ** distribute, sublicense, and/or sell copies of the Materials, and to 16 | ** permit persons to whom the Materials are furnished to do so, subject to 17 | ** the following conditions: 18 | ** 19 | ** The above copyright notice and this permission notice shall be included 20 | ** in all copies or substantial portions of the Materials. 21 | ** 22 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 26 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 27 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 28 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 29 | */ 30 | function stateReset(gl) { 31 | var numAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS) 32 | var tmp = gl.createBuffer() 33 | gl.bindBuffer(gl.ARRAY_BUFFER, tmp) 34 | for (var ii = 0; ii < numAttribs; ++ii) { 35 | gl.disableVertexAttribArray(ii) 36 | gl.vertexAttribPointer(ii, 4, gl.FLOAT, false, 0, 0) 37 | gl.vertexAttrib1f(ii, 0) 38 | } 39 | gl.deleteBuffer(tmp) 40 | 41 | var numTextureUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 42 | for (var ii = 0; ii < numTextureUnits; ++ii) { 43 | gl.activeTexture(gl.TEXTURE0 + ii) 44 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, null) 45 | gl.bindTexture(gl.TEXTURE_2D, null) 46 | } 47 | 48 | gl.activeTexture(gl.TEXTURE0) 49 | gl.useProgram(null) 50 | gl.bindBuffer(gl.ARRAY_BUFFER, null) 51 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null) 52 | gl.bindFramebuffer(gl.FRAMEBUFFER, null) 53 | gl.bindRenderbuffer(gl.RENDERBUFFER, null) 54 | gl.disable(gl.BLEND) 55 | gl.disable(gl.CULL_FACE) 56 | gl.disable(gl.DEPTH_TEST) 57 | gl.disable(gl.DITHER) 58 | gl.disable(gl.SCISSOR_TEST) 59 | gl.blendColor(0, 0, 0, 0) 60 | gl.blendEquation(gl.FUNC_ADD) 61 | gl.blendFunc(gl.ONE, gl.ZERO) 62 | gl.clearColor(0, 0, 0, 0) 63 | gl.clearDepth(1) 64 | gl.clearStencil(-1) 65 | gl.colorMask(true, true, true, true) 66 | gl.cullFace(gl.BACK) 67 | gl.depthFunc(gl.LESS) 68 | gl.depthMask(true) 69 | gl.depthRange(0, 1) 70 | gl.frontFace(gl.CCW) 71 | gl.hint(gl.GENERATE_MIPMAP_HINT, gl.DONT_CARE) 72 | gl.lineWidth(1) 73 | gl.pixelStorei(gl.PACK_ALIGNMENT, 4) 74 | gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4) 75 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false) 76 | gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false) 77 | gl.polygonOffset(0, 0) 78 | gl.sampleCoverage(1, false) 79 | gl.scissor(0, 0, gl.canvas.width, gl.canvas.height) 80 | gl.stencilFunc(gl.ALWAYS, 0, 0xFFFFFFFF) 81 | gl.stencilMask(0xFFFFFFFF) 82 | gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP) 83 | gl.viewport(0, 0, gl.canvas.width, gl.canvas.height) 84 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) 85 | 86 | return gl 87 | } 88 | --------------------------------------------------------------------------------