├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── examples ├── 2d-lines-force-preservedrawingbuffer.html ├── 2d-lines.html ├── js │ ├── twgl-full.min.js │ └── twgl-full.module.js ├── threejs │ ├── index.html │ └── threejs │ │ └── build │ │ └── three.module.js └── unity │ ├── Build │ ├── UnityHTML.data.unityweb │ ├── UnityHTML.json │ ├── UnityHTML.wasm.code.unityweb │ ├── UnityHTML.wasm.framework.unityweb │ └── UnityLoader.js │ ├── TemplateData │ ├── UnityProgress.js │ ├── favicon.ico │ ├── fullscreen.png │ ├── progressEmpty.Dark.png │ ├── progressEmpty.Light.png │ ├── progressFull.Dark.png │ ├── progressFull.Light.png │ ├── progressLogo.Dark.png │ ├── progressLogo.Light.png │ ├── style.css │ └── webgl-logo.png │ ├── index-log-shaders-webgl1-by-disabling-webgl2.html │ ├── index-log-shaders.html │ ├── index-webgl-show-info.html │ └── index.html ├── images ├── dump-shaders-google-maps.png ├── log-draw-calls-jsconsole.gif ├── log-draw-calls.html ├── preservedrawingbuffer.png ├── threejs-log-shaders.png ├── unity-log-shaders.png └── webgl-show-info.png ├── package-lock.json ├── package.json ├── webgl-check-framebuffer-feedback.js ├── webgl-dump-shaders.js ├── webgl-force-alpha-false.js ├── webgl-force-alpha-true.js ├── webgl-force-powerpreference-high-performance.js ├── webgl-force-powerpreference-low-power.js ├── webgl-force-premultipliedalpha-false.js ├── webgl-force-premultipliedalpha-true.js ├── webgl-force-preservedrawingbuffer.js ├── webgl-gl-error-check.js ├── webgl-log-bad-shaders.js ├── webgl-log-shaders.js ├── webgl-show-draw-calls.js ├── webgl-show-info.js └── webgl2-disable.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | }, 6 | "parserOptions": { 7 | "sourceType": "module", 8 | "ecmaVersion": 8, 9 | }, 10 | "plugins": [ 11 | "eslint-plugin-html", 12 | "eslint-plugin-optional-comma-spacing", 13 | "eslint-plugin-one-variable-per-var", 14 | "eslint-plugin-require-trailing-comma", 15 | ], 16 | "extends": "eslint:recommended", 17 | "rules": { 18 | "no-alert": 2, 19 | "no-array-constructor": 2, 20 | "no-caller": 2, 21 | "no-catch-shadow": 2, 22 | "no-const-assign": 2, 23 | "no-eval": 2, 24 | "no-extend-native": 2, 25 | "no-extra-bind": 2, 26 | "no-implied-eval": 2, 27 | "no-inner-declarations": 0, 28 | "no-iterator": 2, 29 | "no-label-var": 2, 30 | "no-labels": 2, 31 | "no-lone-blocks": 0, 32 | "no-multi-str": 2, 33 | "no-native-reassign": 2, 34 | "no-new": 2, 35 | "no-new-func": 2, 36 | "no-new-object": 2, 37 | "no-new-wrappers": 2, 38 | "no-octal-escape": 2, 39 | "no-process-exit": 2, 40 | "no-proto": 2, 41 | "no-return-assign": 2, 42 | "no-script-url": 2, 43 | "no-sequences": 2, 44 | "no-shadow-restricted-names": 2, 45 | "no-spaced-func": 2, 46 | "no-trailing-spaces": 2, 47 | "no-undef-init": 2, 48 | "no-unused-expressions": 2, 49 | "no-use-before-define": 0, 50 | "no-var": 2, 51 | "no-with": 2, 52 | "prefer-const": 2, 53 | "consistent-return": 2, 54 | "curly": [2, "all"], 55 | "no-extra-parens": [2, "functions"], 56 | "eqeqeq": 2, 57 | "new-cap": 2, 58 | "new-parens": 2, 59 | "semi-spacing": [2, {"before": false, "after": true}], 60 | "space-infix-ops": 2, 61 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 62 | "yoda": [2, "never"], 63 | 64 | "brace-style": [2, "1tbs", { "allowSingleLine": false }], 65 | "camelcase": [0], 66 | "comma-spacing": 0, 67 | "comma-dangle": 0, 68 | "comma-style": [2, "last"], 69 | "optional-comma-spacing/optional-comma-spacing": [2, {"after": true}], 70 | "dot-notation": 0, 71 | "eol-last": [0], 72 | "global-strict": [0], 73 | "key-spacing": [0], 74 | "no-comma-dangle": [0], 75 | "no-irregular-whitespace": 2, 76 | "no-multi-spaces": [0], 77 | "no-loop-func": 0, 78 | "no-obj-calls": 2, 79 | "no-redeclare": [0], 80 | "no-shadow": [0], 81 | "no-undef": [2], 82 | "no-unreachable": 2, 83 | "one-variable-per-var/one-variable-per-var": [2], 84 | "quotes": [2, "single"], 85 | "require-atomic-updates": 0, 86 | "require-trailing-comma/require-trailing-comma": [2], 87 | "require-yield": 0, 88 | "semi": [2, "always"], 89 | "strict": [2, "global"], 90 | "space-before-function-paren": [2, "never"], 91 | "keyword-spacing": [1, {"before": true, "after": true, "overrides": {}} ], 92 | }, 93 | }; 94 | 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | .vscode 3 | .travis.yml 4 | images 5 | examples 6 | 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Bufferfi", 4 | "Bufferfv", 5 | "Bufferiv", 6 | "Bufferuiv", 7 | "Framebuffer", 8 | "GMAN", 9 | "Multisample", 10 | "Renderbuffer", 11 | "Renderbuffers", 12 | "emscripten", 13 | "minmax", 14 | "phong", 15 | "powerpreference", 16 | "premultipliedalpha", 17 | "preservedrawingbuffer", 18 | "webgl" 19 | ] 20 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Gregg Tavares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebGL Helpers 2 | 3 | Some scripts and debugger snippets that might come in handy. 4 | 5 | ## Show Info 6 | 7 | Shows how many vertices, instances, and draw calls 8 | are happening per frame as well as the amount of data 9 | being passed to WebGL via functions like `bufferSubData` 10 | and `texSubImage2D`. 11 | 12 | 13 | 14 | See Live Example [here](https://greggman.github.io/webgl-helpers/examples/unity/index-webgl-show-info.html). 15 | 16 | Clicking the info box will show per function counts for the functions being tracked. 17 | 18 | To use, add this script before your other scripts 19 | 20 | ```html 21 | 22 | ``` 23 | 24 | It inserts a `
` in the `` of the page and gives it a CSS class name of `webgl-show-info` 25 | so you can position with with `.webgl-show-info { right: 0; bottom: 0; }` etc... 26 | 27 | Some things to note: 28 | 29 | Certain things are marked in `red`. 30 | 31 | * updating `ELEMENT_ARRAY_BUFFER` buffers can cause perf issues because WebGL is required 32 | to make sure no indices are out of bounds. WebGL implementations usually cache this info 33 | but if you update the buffer they have to invalidate their cache for that buffer. 34 | 35 | Of course if you can't avoid updating indices then you'll have to live with whatever the 36 | perf hit is but if you can redesign so you don't need to update the indices you might find 37 | some perf gains. 38 | 39 | * Calling any `getXXX` function every frame can cause perf issues. Common things are 40 | calling `gl.getUniformLocation` or `gl.getAttribLocation` every frame instead of just 41 | looking them up at init time. The same for example for `gl.checkFramebuffer`. Do it once 42 | at init time. If you need different arrangements of framebuffer attachments then make 43 | multiple framebuffers at init time. 44 | 45 | * Setting up vertex attributes (calling `gl.vertexAttribPointer`, `gl.enableVertexAttribArray`, etc.... 46 | If you want perf you should really be using vertex arrays (ie, `gl.createVertexArray`, `gl.bindVertexArray`). 47 | To be honest, every WebGL app I've ever written breaks this rule because WebGL1 didn't support 48 | vertex arrays without an extension. 49 | 50 | Also, remember that **Premature Optimization is the root of all evil**. The majority of WebGL out there 51 | just doesn't do enough work that these optimizations will matter. But, if you happen to be getting near 52 | the limits then these are places you might look for perf gains. 53 | 54 | Remember though, find the biggest perf issues first. If you have lots of overdraw, or slow complex shaders, 55 | or a complex post processing pipeline doing a bunch of passes, or you're just drawing way to much stuff, 56 | the thing above are probably not your bottleneck. 57 | 58 | Here's a script you can paste into the JavaScript console to use on a running page 59 | 60 | ```js 61 | (()=>{const s = document.createElement('script');s.src='https://greggman.github.io/webgl-helpers/webgl-show-info.js';document.firstElementChild.appendChild(s)})(); 62 | ``` 63 | 64 | ## glEnumToString 65 | 66 | A simple one, incomplete, but useful in a pinch. 67 | 68 | ``` 69 | function glEnumToString(value) { 70 | const keys = []; 71 | for (const key in WebGL2RenderingContext) { 72 | if (WebGL2RenderingContext[key] === value) { 73 | keys.push(key); 74 | } 75 | } 76 | return keys.length ? keys.join(' | ') : `0x${value.toString(16)}`; 77 | } 78 | ``` 79 | 80 | The issue with it being incomplete it some enums are specified on extensions. 81 | One that covers all enums is a little too involved. Also, GL unfortunately 82 | chose `0` for 4 different values. `NONE`, `POINTS`, `FALSE`, `NO_ERROR` which 83 | is why the `join` above. Otherwise you'd need to know the function the value 84 | is going to be used with in order to return the correct string. 85 | 86 | ## Show the available extensions 87 | 88 | ``` 89 | console.log(document.createElement("canvas").getContext("webgl").getSupportedExtensions().join('\n')); 90 | console.log(document.createElement("canvas").getContext("webgl2").getSupportedExtensions().join('\n')); 91 | ``` 92 | 93 | ## Show the VENDOR / RENDERER 94 | 95 | Show both `low-power` and `high-performance` 96 | 97 | ``` 98 | { 99 | for (const powerPreference of ["low-power", "high-performance"]) { 100 | const gl = document.createElement("canvas").getContext("webgl"); 101 | const ext = gl.getExtension("WEBGL_debug_renderer_info"); 102 | console.log(`${powerPreference}\n${["VENDOR", "RENDERER", "UNMASKED_RENDERER_WEBGL", "UNMASKED_VENDOR_WEBGL"].map(name => { 103 | const pname = ext[name] || gl[name]; 104 | return pname ? `${name}: ${gl.getParameter(pname)}` : ""; 105 | }).filter(v => v).join('\n')}`); 106 | } 107 | } 108 | ``` 109 | 110 | Show the default (usually `low-power` but up to the browser). Also, if you have a variable `gl` or some other context just delete the first line or change to `gl = ` 111 | 112 | ``` 113 | { 114 | const gl = document.createElement("canvas").getContext("webgl"); 115 | const ext = gl.getExtension("WEBGL_debug_renderer_info"); 116 | console.log(["VENDOR", "RENDERER", "UNMASKED_RENDERER_WEBGL", "UNMASKED_VENDOR_WEBGL"].map(name => { 117 | const pname = ext[name] || gl[name]; 118 | return pname ? `${name}: ${gl.getParameter(pname)}` : ""; 119 | }).filter(v => v).join('\n')); 120 | } 121 | ``` 122 | 123 | 124 | ## Print out *most* of the limits 125 | 126 | ``` 127 | { 128 | const gl = document.createElement('canvas').getContext('webgl2'); 129 | const m = {}; 130 | for (const key in gl) { 131 | if (key.startsWith("MAX_")) { 132 | m[key] = gl.getParameter(gl[key]); 133 | } 134 | } 135 | console.log(JSON.stringify(m, null, 2)); 136 | } 137 | ``` 138 | 139 | You could just go to [webglreport.com](https://webglreport.com) to see the limits but I needed to be able to compare. 140 | 141 | ``` 142 | a = 143 | b = 144 | console.log(Object.entries(a).map(([k, v]) => `${v.toString().padStart(10)} ${b[k].toString().padStart(10)} : ${k}`).join('\n')) 145 | ``` 146 | 147 | ## Spy on draw calls 148 | 149 | Copy and paste this into the JavaScript console 150 | 151 | ``` 152 | (()=>{const s = document.createElement('script');s.src='https://greggman.github.io/webgl-helpers/webgl-show-draw-calls.js';document.firstElementChild.appendChild(s)})(); 153 | ``` 154 | 155 | or copy and pasted [the entire file](https://raw.githubusercontent.com/greggman/webgl-helpers/master/webgl-show-draw-calls.js) into the JavaScript console. 156 | 157 | Example, select the correct context, then copy and paste 158 | 159 | 160 | 161 | Or use 162 | 163 | ``` 164 | 165 | ``` 166 | 167 | # scripts to use when you're including a 3rd party WebGL lib (Unity, three.js, etc...) 168 | 169 | ## webgl-log-shaders.js 170 | 171 | Want to dump shaders, add this script at the top of your HTML file 172 | 173 | ``` 174 | 175 | ``` 176 | 177 | For example [here's a Unity example with the script above added to the top of the HTML file](https://greggman.github.io/webgl-helpers/examples/unity/index-log-shaders.html). 178 | 179 | 180 | 181 | And here's [the same with three.js](https://greggman.github.io/webgl-helpers/examples/threejs/). 182 | 183 | 184 | 185 | ## webgl-bad-log-shaders.js 186 | 187 | Same as above but only logs a shader if it fails to compile. This can be useful 188 | if you have a big project like a Unity project and you want to extract the 189 | shader to file an [MCVE bug report](https://en.wikipedia.org/wiki/Minimal_reproducible_example). 190 | 191 | Add this to the top of your HTML file 192 | 193 | ``` 194 | 195 | ``` 196 | 197 | If a shader fails to compile or a program fails to link it will print an error and their source code to the console. 198 | 199 | ## webgl-dump-shaders.js 200 | 201 | Same as above except you can possibly copy and paste this contents into the JS console. 202 | 203 | ``` 204 | (()=>{const s = document.createElement('script');s.src='https://greggman.github.io/webgl-helpers/webgl-dump-shaders.js';document.firstElementChild.appendChild(s)})(); 205 | ``` 206 | 207 | For example Google Maps 208 | 209 | 210 | 211 | Note: This doesn't always work if the app deletes/detaches its shaders from its programs. Another method to dump shaders 212 | is to open the JavaScript console, add a breakpoint early in the page's JavaScript. When the breakpoint hits, 213 | paste the following into the JavaScript console. 214 | 215 | ```js 216 | WebGL2RenderingContext.prototype.shaderSource = (function(origFn) { 217 | return function(shader, source) { 218 | origFn.call(this, shader, source); 219 | console.log(source); 220 | }; 221 | })(WebGL2RenderingContext.prototype.shaderSource); 222 | WebGLRenderingContext.prototype.shaderSource = (function(origFn) { 223 | return function(shader, source) { 224 | origFn.call(this, shader, source); 225 | console.log(source); 226 | }; 227 | })(WebGLRenderingContext.prototype.shaderSource); 228 | ``` 229 | 230 | The continue the JavaScript execution. 231 | 232 | ## webgl-disable2.js 233 | 234 | Disables WebGL2. Useful to force something to use WebGL1 assuming it can handle both 235 | 236 | ``` 237 | 238 | ``` 239 | 240 | ## webgl-force-preservedrawingbuffer.js 241 | 242 | Forces `preserveDrawingBuffer: true`. 243 | 244 | Maybe you want to take a screenshot of some canvas that another script is controlling. 245 | 246 | ``` 247 | 248 | ``` 249 | 250 | Example: 251 | 252 | * [without script](https://greggman.github.io/webgl-helpers/examples/2d-lines.html) 253 | * [with script](https://greggman.github.io/webgl-helpers/examples/2d-lines-force-preservedrawingbuffer.html) 254 | 255 | 256 | 257 | ## webgl-force-alpha-true.js 258 | ## webgl-force-alpha-false.js 259 | 260 | Forces `alpha: true` or `alpha: false` 261 | 262 | Could be useful if you can't figure out how to get a certain library to 263 | be one or the other. For example the myriad of poorly documented ways 264 | that emscripten creates a canvas. 265 | 266 | ``` 267 | 268 | ``` 269 | 270 | ``` 271 | 272 | ``` 273 | 274 | ## webgl-force-premultipliedalpha-true.js 275 | ## webgl-force-premultipliedalpha-false.js 276 | 277 | Forces `premultipliedAlpha: true` or `premultipliedAlpha: false` 278 | 279 | Could be useful if you can't figure out how to get a certain library to 280 | be one or the other. For example the myriad of poorly documented ways 281 | that emscripten creates a canvas. 282 | 283 | ``` 284 | 285 | ``` 286 | 287 | ``` 288 | 289 | ``` 290 | 291 | ## webgl-force-powerpreference-low-power.js 292 | ## webgl-force-powerpreference-high-performance.js 293 | 294 | Forces the powerPreference setting. 295 | 296 | Could be useful if the library you're using has no way to set this 297 | and you want it to be something other than the default. 298 | 299 | ``` 300 | 301 | ``` 302 | 303 | ``` 304 | 305 | ``` 306 | 307 | ## webgl-gl-error-check.js 308 | 309 | This script has moved to [https://github.com/greggman/webgl-lint](https://github.com/greggman/webgl-lint) 310 | 311 | # Other useful things 312 | 313 | * [virtual-webgl](https://github.com/greggman/virtual-webgl): A library to virtualize WebGL to surpass the context limit 314 | * [webgl-capture](https://github.com/greggman/webgl-capture/): A library to capture GL calls into a reproducible webpage 315 | * [twgl](https://twgljs.org): A library to simplify using WebGL 316 | -------------------------------------------------------------------------------- /examples/2d-lines-force-preservedrawingbuffer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 17 | 18 | 19 | 20 | 21 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /examples/2d-lines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | 19 | 20 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /examples/js/twgl-full.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @license twgl.js 4.14.0 Copyright (c) 2015, Gregg Tavares All Rights Reserved. 3 | * Available via the MIT license. 4 | * see: http://github.com/greggman/twgl.js for details 5 | */ 6 | !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.twgl=r():e.twgl=r()}("undefined"!=typeof self?self:this,(function(){return function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=8)}([function(e,r,t){"use strict";r.__esModule=!0,r.copyExistingProperties=function(e,r){Object.keys(r).forEach((function(t){r.hasOwnProperty(t)&&e.hasOwnProperty(t)&&(r[t]=e[t])}))},r.copyNamedProperties=function(e,r,t){e.forEach((function(e){var n=r[e];void 0!==n&&(t[e]=n)}))},r.error=function(){var e;(e=console).error.apply(e,arguments)},r.warn=function(){var e;(e=console).warn.apply(e,arguments)},r.isBuffer=function(e,r){return"undefined"!=typeof WebGLBuffer&&r instanceof WebGLBuffer},r.isRenderbuffer=function(e,r){return"undefined"!=typeof WebGLRenderbuffer&&r instanceof WebGLRenderbuffer},r.isShader=function(e,r){return"undefined"!=typeof WebGLShader&&r instanceof WebGLShader},r.isTexture=function(e,r){return"undefined"!=typeof WebGLTexture&&r instanceof WebGLTexture},r.isSampler=function(e,r){return"undefined"!=typeof WebGLSampler&&r instanceof WebGLSampler}},function(e,r,t){"use strict";r.__esModule=!0,r.getGLTypeForTypedArray=function(e){if(e instanceof Int8Array)return n;if(e instanceof Uint8Array)return o;if(e instanceof Uint8ClampedArray)return o;if(e instanceof Int16Array)return u;if(e instanceof Uint16Array)return i;if(e instanceof Int32Array)return a;if(e instanceof Uint32Array)return f;if(e instanceof Float32Array)return c;throw new Error("unsupported typed array type")},r.getGLTypeForTypedArrayType=function(e){if(e===Int8Array)return n;if(e===Uint8Array)return o;if(e===Uint8ClampedArray)return o;if(e===Int16Array)return u;if(e===Uint16Array)return i;if(e===Int32Array)return a;if(e===Uint32Array)return f;if(e===Float32Array)return c;throw new Error("unsupported typed array type")},r.getTypedArrayTypeForGLType=function(e){var r=l[e];if(!r)throw new Error("unknown gl type");return r},r.isArrayBuffer=void 0;var n=5120,o=5121,u=5122,i=5123,a=5124,f=5125,c=5126,l={},s=l;s[n]=Int8Array,s[o]=Uint8Array,s[u]=Int16Array,s[i]=Uint16Array,s[a]=Int32Array,s[f]=Uint32Array,s[c]=Float32Array,s[32819]=Uint16Array,s[32820]=Uint16Array,s[33635]=Uint16Array,s[5131]=Uint16Array,s[33640]=Uint32Array,s[35899]=Uint32Array,s[35902]=Uint32Array,s[36269]=Uint32Array,s[34042]=Uint32Array;var v="undefined"!=typeof SharedArrayBuffer?function(e){return e&&e.buffer&&(e.buffer instanceof ArrayBuffer||e.buffer instanceof SharedArrayBuffer)}:function(e){return e&&e.buffer&&e.buffer instanceof ArrayBuffer};r.isArrayBuffer=v},function(e,r,t){"use strict";r.__esModule=!0,r.add=function(e,r,t){return(t=t||new n(3))[0]=e[0]+r[0],t[1]=e[1]+r[1],t[2]=e[2]+r[2],t},r.copy=function(e,r){return(r=r||new n(3))[0]=e[0],r[1]=e[1],r[2]=e[2],r},r.create=function(e,r,t){var o=new n(3);e&&(o[0]=e);r&&(o[1]=r);t&&(o[2]=t);return o},r.cross=function(e,r,t){t=t||new n(3);var o=e[2]*r[0]-e[0]*r[2],u=e[0]*r[1]-e[1]*r[0];return t[0]=e[1]*r[2]-e[2]*r[1],t[1]=o,t[2]=u,t},r.distance=function(e,r){var t=e[0]-r[0],n=e[1]-r[1],o=e[2]-r[2];return Math.sqrt(t*t+n*n+o*o)},r.distanceSq=function(e,r){var t=e[0]-r[0],n=e[1]-r[1],o=e[2]-r[2];return t*t+n*n+o*o},r.divide=function(e,r,t){return(t=t||new n(3))[0]=e[0]/r[0],t[1]=e[1]/r[1],t[2]=e[2]/r[2],t},r.divScalar=function(e,r,t){return(t=t||new n(3))[0]=e[0]/r,t[1]=e[1]/r,t[2]=e[2]/r,t},r.dot=function(e,r){return e[0]*r[0]+e[1]*r[1]+e[2]*r[2]},r.lerp=function(e,r,t,o){return(o=o||new n(3))[0]=e[0]+t*(r[0]-e[0]),o[1]=e[1]+t*(r[1]-e[1]),o[2]=e[2]+t*(r[2]-e[2]),o},r.lerpV=function(e,r,t,o){return(o=o||new n(3))[0]=e[0]+t[0]*(r[0]-e[0]),o[1]=e[1]+t[1]*(r[1]-e[1]),o[2]=e[2]+t[2]*(r[2]-e[2]),o},r.length=function(e){return Math.sqrt(e[0]*e[0]+e[1]*e[1]+e[2]*e[2])},r.lengthSq=function(e){return e[0]*e[0]+e[1]*e[1]+e[2]*e[2]},r.max=function(e,r,t){return(t=t||new n(3))[0]=Math.max(e[0],r[0]),t[1]=Math.max(e[1],r[1]),t[2]=Math.max(e[2],r[2]),t},r.min=function(e,r,t){return(t=t||new n(3))[0]=Math.min(e[0],r[0]),t[1]=Math.min(e[1],r[1]),t[2]=Math.min(e[2],r[2]),t},r.mulScalar=function(e,r,t){return(t=t||new n(3))[0]=e[0]*r,t[1]=e[1]*r,t[2]=e[2]*r,t},r.multiply=function(e,r,t){return(t=t||new n(3))[0]=e[0]*r[0],t[1]=e[1]*r[1],t[2]=e[2]*r[2],t},r.negate=function(e,r){return(r=r||new n(3))[0]=-e[0],r[1]=-e[1],r[2]=-e[2],r},r.normalize=function(e,r){r=r||new n(3);var t=e[0]*e[0]+e[1]*e[1]+e[2]*e[2],o=Math.sqrt(t);o>1e-5?(r[0]=e[0]/o,r[1]=e[1]/o,r[2]=e[2]/o):(r[0]=0,r[1]=0,r[2]=0);return r},r.setDefaultType=function(e){var r=n;return n=e,r},r.subtract=function(e,r,t){return(t=t||new n(3))[0]=e[0]-r[0],t[1]=e[1]-r[1],t[2]=e[2]-r[2],t};var n=Float32Array},function(e,r,t){"use strict";r.__esModule=!0,r.isWebGL1=function(e){return!e.texStorage2D},r.isWebGL2=function(e){return!!e.texStorage2D},r.glEnumToString=void 0;var n,o,u=(n={},o={},function(e,r){return function(e){var r=e.constructor.name;if(!n[r]){for(var t in e)if("number"==typeof e[t]){var u=o[e[t]];o[e[t]]=u?"".concat(u," | ").concat(t):t}n[r]=!0}}(e),o[r]||"0x"+r.toString(16)});r.glEnumToString=u},function(e,r,t){"use strict";r.__esModule=!0,r.createAttributeSetters=ve,r.createProgram=Q,r.createProgramFromScripts=function(e,r,t,n,o){for(var u=K(t,n,o),i=[],a=0;a=0?w:r.indexOf("vert")>=0?h:void 0}function J(e,r){r.forEach((function(r){e.deleteShader(r)}))}function Q(e,r,t,n,u){for(var i=K(t,n,u),a=[],f=[],l=0;l1&&"[0]"===n.name.substr(-3),a=n.type,f=U[a];if(!f)throw new Error("unknown type: 0x".concat(a.toString(16)));if(f.bindPoint){var c=t;t+=n.size,o=i?f.arraySetter(e,a,c,u,n.size):f.setter(e,a,c,u,n.size)}else o=f.arraySetter&&i?f.arraySetter(e,u):f.setter(e,u);return o.location=u,o}for(var o={},u=e.getProgramParameter(r,E),i=0;i0)throw new Error("numComponents ".concat(u," not correct for length ").concat(o));return i}(r);return t},r.createBufferFromArray=T,r.createBufferFromTypedArray=h,r.createBufferInfoFromArrays=function(e,r,t){var o=P(e,r),u=Object.assign({},t||{});u.attribs=Object.assign({},t?t.attribs:{},o);var i=r.indices;if(i){var a=M(i,"indices");u.indices=h(e,a,c),u.numElements=a.length,u.elementType=n.getGLTypeForTypedArray(a)}else u.numElements||(u.numElements=function(e,r){var t,n;for(n=0;n0)throw new Error("Can not guess numComponents for attribute '".concat(e,"'. Tried ").concat(t," but ").concat(r," values is not evenly divisible by ").concat(t,". You should specify it."));return t}function j(e,r){return e.numComponents||e.size||_(r,E(e).length)}function M(e,r){if(n.isArrayBuffer(e))return e;if(n.isArrayBuffer(e.data))return e.data;Array.isArray(e)&&(e={data:e});var t=e.type;return t||(t=F(r)?Uint16Array:Float32Array),new t(e.data)}function P(e,r){var t={};return Object.keys(r).forEach((function(o){if(!F(o)){var u=r[o],i=u.attrib||u.name||u.attribName||x.attribPrefix+o;if(u.value){if(!Array.isArray(u.value)&&!n.isArrayBuffer(u.value))throw new Error("array.value is not array or typedarray");t[i]={value:u.value}}else{var c,l,s,v;if(u.buffer&&u.buffer instanceof WebGLBuffer)c=u.buffer,v=u.numComponents||u.size,l=u.type,s=u.normalize;else if("number"==typeof u||"number"==typeof u.data){var b=u.data||u,y=u.type||Float32Array,d=b*y.BYTES_PER_ELEMENT;l=n.getGLTypeForTypedArrayType(y),s=void 0!==u.normalize?u.normalize:(p=y)===Int8Array||p===Uint8Array,v=u.numComponents||u.size||_(o,b),c=e.createBuffer(),e.bindBuffer(f,c),e.bufferData(f,d,u.drawType||a)}else{var m=M(u,o);c=h(e,m,void 0,u.drawType),l=n.getGLTypeForTypedArray(m),s=void 0!==u.normalize?u.normalize:function(e){return e instanceof Int8Array||e instanceof Uint8Array}(m),v=j(u,o)}t[i]={buffer:c,numComponents:v,type:l,normalize:s,stride:u.stride||0,offset:u.offset||0,divisor:void 0===u.divisor?void 0:u.divisor,drawType:u.drawType}}}var p})),e.bindBuffer(f,null),t}var R=["position","positions","a_position"];function T(e,r,t){var n="indices"===t?c:f;return h(e,M(r,t),n)}},function(e,r,t){"use strict";r.__esModule=!0,r.setTextureDefaults_=function(e){u.copyExistingProperties(e,c),e.textureColor&&Tr(e.textureColor)},r.createSampler=Br,r.createSamplers=function(e,r){var t={};return Object.keys(r).forEach((function(n){t[n]=Br(e,r[n])})),t},r.setSamplerParameters=Ir,r.createTexture=rt,r.setEmptyTexture=et,r.setTextureFromArray=Qr,r.loadTextureFromUrl=Zr,r.setTextureFromElement=Nr,r.setTextureFilteringForSize=Lr,r.setTextureParameters=Wr,r.setDefaultTextureColor=Tr,r.createTextures=function(e,r,t){t=t||Hr;var n=0,o=[],u={},i={};function a(){0===n&&setTimeout((function(){t(o.length?o:void 0,u,i)}),0)}return Object.keys(r).forEach((function(t){var f,c,l=r[t];("string"==typeof(c=l.src)||Array.isArray(c)&&"string"==typeof c[0])&&(f=function(e,r,u){i[t]=u,--n,e&&o.push(e),a()},++n),u[t]=rt(e,l,f)})),a(),u},r.resizeTexture=function(e,r,t,n,o,u){n=n||t.width,o=o||t.height,u=u||t.depth;var i=t.target||A;e.bindTexture(i,r);var a,f=t.level||0,c=t.internalFormat||t.format||d,s=Or(c),v=t.format||s.format,b=t.src;a=b&&(l(b)||Array.isArray(b)&&"number"==typeof b[0])?t.type||Pr(e,b,s.type):t.type||s.type;if(i===O)for(var y=0;y<6;++y)e.texImage2D(M+y,f,c,n,o,0,v,a,null);else i===_||i===j?e.texImage3D(i,f,c,n,o,u,0,v,a,null):e.texImage2D(i,f,c,n,o,0,v,a,null)},r.canGenerateMipmap=jr,r.canFilter=Mr,r.getNumComponentsForFormat=function(e){var r=hr[e];if(!r)throw"unknown format: "+e;return r.u},r.getBytesPerElementForInternalFormat=Ar,r.getFormatAndTypeForInternalFormat=Or;var n=a(t(3)),o=a(t(1)),u=a(t(0));function i(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return i=function(){return e},e}function a(e){if(e&&e.__esModule)return e;var r=i();if(r&&r.has(e))return r.get(e);var t={};if(null!=e){var n=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var u=n?Object.getOwnPropertyDescriptor(e,o):null;u&&(u.get||u.set)?Object.defineProperty(t,o,u):t[o]=e[o]}}return t.default=e,r&&r.set(e,t),t}var f,c={textureColor:new Uint8Array([128,192,255,255]),textureOptions:{},crossOrigin:void 0},l=o.isArrayBuffer;function s(){return f=f||("undefined"!=typeof document&&document.createElement?document.createElement("canvas").getContext("2d"):null)}var v,b=6406,y=6407,d=6408,m=6409,p=6410,x=6402,w=34041,h=33071,F=9728,E=9729,A=3553,O=34067,_=32879,j=35866,M=34069,P=34070,R=34071,T=34072,g=34073,z=34074,U=10241,C=10240,S=10242,k=10243,W=32882,I=33082,B=33083,L=33084,G=33085,X=3317,D=3314,N=32878,H=3316,q=3315,V=32877,Y=37443,K=37441,Z=37440,$=33321,J=36756,Q=33325,ee=33326,re=33330,te=33329,ne=33338,oe=33337,ue=33340,ie=33339,ae=33323,fe=36757,ce=33327,le=33328,se=33336,ve=33335,be=33332,ye=33331,de=33334,me=33333,pe=32849,xe=35905,we=36194,he=36758,Fe=35898,Ee=35901,Ae=34843,Oe=34837,_e=36221,je=36239,Me=36215,Pe=36233,Re=36209,Te=36227,ge=32856,ze=35907,Ue=36759,Ce=32855,Se=32854,ke=32857,We=34842,Ie=34836,Be=36220,Le=36238,Ge=36975,Xe=36214,De=36232,Ne=36226,He=36208,qe=33189,Ve=33190,Ye=36012,Ke=36013,Ze=35056,$e=5120,Je=5121,Qe=5122,er=5123,rr=5124,tr=5125,nr=5126,or=32819,ur=32820,ir=33635,ar=5131,fr=36193,cr=33640,lr=35899,sr=35902,vr=36269,br=34042,yr=33319,dr=33320,mr=6403,pr=36244,xr=36248,wr=36249,hr={},Fr=hr;function Er(e){if(!v){var r={};r[b]={v:b,h:!0,F:!0,A:[1,2,2,4],type:[Je,ar,fr,nr]},r[m]={v:m,h:!0,F:!0,A:[1,2,2,4],type:[Je,ar,fr,nr]},r[p]={v:p,h:!0,F:!0,A:[2,4,4,8],type:[Je,ar,fr,nr]},r[y]={v:y,h:!0,F:!0,A:[3,6,6,12,2],type:[Je,ar,fr,nr,ir]},r[d]={v:d,h:!0,F:!0,A:[4,8,8,16,2,2],type:[Je,ar,fr,nr,or,ur]},r[$]={v:mr,h:!0,F:!0,A:[1],type:[Je]},r[J]={v:mr,h:!1,F:!0,A:[1],type:[$e]},r[Q]={v:mr,h:!1,F:!0,A:[4,2],type:[nr,ar]},r[ee]={v:mr,h:!1,F:!1,A:[4],type:[nr]},r[re]={v:pr,h:!0,F:!1,A:[1],type:[Je]},r[te]={v:pr,h:!0,F:!1,A:[1],type:[$e]},r[be]={v:pr,h:!0,F:!1,A:[2],type:[er]},r[ye]={v:pr,h:!0,F:!1,A:[2],type:[Qe]},r[de]={v:pr,h:!0,F:!1,A:[4],type:[tr]},r[me]={v:pr,h:!0,F:!1,A:[4],type:[rr]},r[ae]={v:yr,h:!0,F:!0,A:[2],type:[Je]},r[fe]={v:yr,h:!1,F:!0,A:[2],type:[$e]},r[ce]={v:yr,h:!1,F:!0,A:[8,4],type:[nr,ar]},r[le]={v:yr,h:!1,F:!1,A:[8],type:[nr]},r[se]={v:dr,h:!0,F:!1,A:[2],type:[Je]},r[ve]={v:dr,h:!0,F:!1,A:[2],type:[$e]},r[ne]={v:dr,h:!0,F:!1,A:[4],type:[er]},r[oe]={v:dr,h:!0,F:!1,A:[4],type:[Qe]},r[ue]={v:dr,h:!0,F:!1,A:[8],type:[tr]},r[ie]={v:dr,h:!0,F:!1,A:[8],type:[rr]},r[pe]={v:y,h:!0,F:!0,A:[3],type:[Je]},r[xe]={v:y,h:!1,F:!0,A:[3],type:[Je]},r[we]={v:y,h:!0,F:!0,A:[3,2],type:[Je,ir]},r[he]={v:y,h:!1,F:!0,A:[3],type:[$e]},r[Fe]={v:y,h:!1,F:!0,A:[12,6,4],type:[nr,ar,lr]},r[Ee]={v:y,h:!1,F:!0,A:[12,6,4],type:[nr,ar,sr]},r[Ae]={v:y,h:!1,F:!0,A:[12,6],type:[nr,ar]},r[Oe]={v:y,h:!1,F:!1,A:[12],type:[nr]},r[_e]={v:xr,h:!1,F:!1,A:[3],type:[Je]},r[je]={v:xr,h:!1,F:!1,A:[3],type:[$e]},r[Me]={v:xr,h:!1,F:!1,A:[6],type:[er]},r[Pe]={v:xr,h:!1,F:!1,A:[6],type:[Qe]},r[Re]={v:xr,h:!1,F:!1,A:[12],type:[tr]},r[Te]={v:xr,h:!1,F:!1,A:[12],type:[rr]},r[ge]={v:d,h:!0,F:!0,A:[4],type:[Je]},r[ze]={v:d,h:!0,F:!0,A:[4],type:[Je]},r[Ue]={v:d,h:!1,F:!0,A:[4],type:[$e]},r[Ce]={v:d,h:!0,F:!0,A:[4,2,4],type:[Je,ur,cr]},r[Se]={v:d,h:!0,F:!0,A:[4,2],type:[Je,or]},r[ke]={v:d,h:!0,F:!0,A:[4],type:[cr]},r[We]={v:d,h:!1,F:!0,A:[16,8],type:[nr,ar]},r[Ie]={v:d,h:!1,F:!1,A:[16],type:[nr]},r[Be]={v:wr,h:!0,F:!1,A:[4],type:[Je]},r[Le]={v:wr,h:!0,F:!1,A:[4],type:[$e]},r[Ge]={v:wr,h:!0,F:!1,A:[4],type:[cr]},r[Xe]={v:wr,h:!0,F:!1,A:[8],type:[er]},r[De]={v:wr,h:!0,F:!1,A:[8],type:[Qe]},r[Ne]={v:wr,h:!0,F:!1,A:[16],type:[rr]},r[He]={v:wr,h:!0,F:!1,A:[16],type:[tr]},r[qe]={v:x,h:!0,F:!1,A:[2,4],type:[er,tr]},r[Ve]={v:x,h:!0,F:!1,A:[4],type:[tr]},r[Ye]={v:x,h:!0,F:!1,A:[4],type:[nr]},r[Ze]={v:w,h:!0,F:!1,A:[4],type:[br]},r[Ke]={v:w,h:!0,F:!1,A:[4],type:[vr]},Object.keys(r).forEach((function(e){var t=r[e];t.bytesPerElementMap={},t.A.forEach((function(e,r){var n=t.type[r];t.bytesPerElementMap[n]=e}))})),v=r}return v[e]}function Ar(e,r){var t=Er(e);if(!t)throw"unknown internal format";var n=t.bytesPerElementMap[r];if(void 0===n)throw"unknown internal format";return n}function Or(e){var r=Er(e);if(!r)throw"unknown internal format";return{format:r.v,type:r.type[0]}}function _r(e){return 0==(e&e-1)}function jr(e,r,t,o){if(!n.isWebGL2(e))return _r(r)&&_r(t);var u=Er(o);if(!u)throw"unknown internal format";return u.h&&u.F}function Mr(e){var r=Er(e);if(!r)throw"unknown internal format";return r.F}function Pr(e,r,t){return l(r)?o.getGLTypeForTypedArray(r):t||Je}function Rr(e,r,t,n,o){if(o%1!=0)throw"can't guess dimensions";if(t||n){if(n){if(!t&&(t=o/n)%1)throw"can't guess dimensions"}else if((n=o/t)%1)throw"can't guess dimensions"}else{var u=Math.sqrt(o/(r===O?6:1));u%1==0?(t=u,n=u):(t=o,n=1)}return{width:t,height:n}}function Tr(e){c.textureColor=new Uint8Array([255*e[0],255*e[1],255*e[2],255*e[3]])}Fr[b]={u:1},Fr[m]={u:1},Fr[p]={u:2},Fr[y]={u:3},Fr[d]={u:4},Fr[mr]={u:1},Fr[pr]={u:1},Fr[yr]={u:2},Fr[dr]={u:2},Fr[y]={u:3},Fr[xr]={u:3},Fr[d]={u:4},Fr[wr]={u:4},Fr[x]={u:1},Fr[w]={u:2};var gr={};function zr(e,r){void 0!==r.colorspaceConversion&&(gr.colorspaceConversion=e.getParameter(Y),e.pixelStorei(Y,r.colorspaceConversion)),void 0!==r.premultiplyAlpha&&(gr.premultiplyAlpha=e.getParameter(K),e.pixelStorei(K,r.premultiplyAlpha)),void 0!==r.flipY&&(gr.flipY=e.getParameter(Z),e.pixelStorei(Z,r.flipY))}function Ur(e,r){void 0!==r.colorspaceConversion&&e.pixelStorei(Y,gr.colorspaceConversion),void 0!==r.premultiplyAlpha&&e.pixelStorei(K,gr.premultiplyAlpha),void 0!==r.flipY&&e.pixelStorei(Z,gr.flipY)}function Cr(e){gr.unpackAlignment=e.getParameter(X),n.isWebGL2(e)&&(gr.unpackRowLength=e.getParameter(D),gr.unpackImageHeight=e.getParameter(N),gr.unpackSkipPixels=e.getParameter(H),gr.unpackSkipRows=e.getParameter(q),gr.unpackSkipImages=e.getParameter(V))}function Sr(e){e.pixelStorei(X,gr.unpackAlignment),n.isWebGL2(e)&&(e.pixelStorei(D,gr.unpackRowLength),e.pixelStorei(N,gr.unpackImageHeight),e.pixelStorei(H,gr.unpackSkipPixels),e.pixelStorei(q,gr.unpackSkipRows),e.pixelStorei(V,gr.unpackSkipImages))}function kr(e,r,t,n){n.minMag&&(t.call(e,r,U,n.minMag),t.call(e,r,C,n.minMag)),n.min&&t.call(e,r,U,n.min),n.mag&&t.call(e,r,C,n.mag),n.wrap&&(t.call(e,r,S,n.wrap),t.call(e,r,k,n.wrap),(r===_||u.isSampler(e,r))&&t.call(e,r,W,n.wrap)),n.wrapR&&t.call(e,r,W,n.wrapR),n.wrapS&&t.call(e,r,S,n.wrapS),n.wrapT&&t.call(e,r,k,n.wrapT),n.minLod&&t.call(e,r,I,n.minLod),n.maxLod&&t.call(e,r,B,n.maxLod),n.baseLevel&&t.call(e,r,L,n.baseLevel),n.maxLevel&&t.call(e,r,G,n.maxLevel)}function Wr(e,r,t){var n=t.target||A;e.bindTexture(n,r),kr(e,n,e.texParameteri,t)}function Ir(e,r,t){kr(e,r,e.samplerParameteri,t)}function Br(e,r){var t=e.createSampler();return Ir(e,t,r),t}function Lr(e,r,t,n,o,u){t=t||c.textureOptions,u=u||d;var i=t.target||A;if(n=n||t.width,o=o||t.height,e.bindTexture(i,r),jr(e,n,o,u))e.generateMipmap(i);else{var a=Mr(u)?E:F;e.texParameteri(i,U,a),e.texParameteri(i,C,a),e.texParameteri(i,S,h),e.texParameteri(i,k,h)}}function Gr(e){return!0===e.auto||void 0===e.auto&&void 0===e.level}function Xr(e,r){return(r=r||{}).cubeFaceOrder||[M,P,R,T,g,z]}function Dr(e,r){var t=Xr(0,r).map((function(e,r){return{face:e,ndx:r}}));return t.sort((function(e,r){return e.face-r.face})),t}function Nr(e,r,t,n){var o=(n=n||c.textureOptions).target||A,u=n.level||0,i=t.width,a=t.height,f=n.internalFormat||n.format||d,l=Or(f),v=n.format||l.format,b=n.type||l.type;if(zr(e,n),e.bindTexture(o,r),o===O){var y,m,p=t.width,x=t.height;if(p/6===x)y=x,m=[0,0,1,0,2,0,3,0,4,0,5,0];else if(x/6===p)y=p,m=[0,0,0,1,0,2,0,3,0,4,0,5];else if(p/3==x/2)y=p/3,m=[0,0,1,0,2,0,0,1,1,1,2,1];else{if(p/2!=x/3)throw"can't figure out cube map from element: "+(t.src?t.src:t.nodeName);y=p/2,m=[0,0,1,0,0,1,1,1,0,2,1,2]}var w=s();w?(w.canvas.width=y,w.canvas.height=y,i=y,a=y,Dr(0,n).forEach((function(r){var n=m[2*r.ndx+0]*y,o=m[2*r.ndx+1]*y;w.drawImage(t,n,o,y,y,0,0,y,y),e.texImage2D(r.face,u,f,v,b,w.canvas)})),w.canvas.width=1,w.canvas.height=1):"undefined"!=typeof createImageBitmap&&(i=y,a=y,Dr(0,n).forEach((function(c){var l=m[2*c.ndx+0]*y,s=m[2*c.ndx+1]*y;e.texImage2D(c.face,u,f,y,y,0,v,b,null),createImageBitmap(t,l,s,y,y,{premultiplyAlpha:"none",colorSpaceConversion:"none"}).then((function(t){zr(e,n),e.bindTexture(o,r),e.texImage2D(c.face,u,f,v,b,t),Ur(e,n),Gr(n)&&Lr(e,r,n,i,a,f)}))})))}else if(o===_||o===j){var h=Math.min(t.width,t.height),F=Math.max(t.width,t.height),E=F/h;if(E%1!=0)throw"can not compute 3D dimensions of element";var M=t.width===F?1:0,P=t.height===F?1:0;Cr(e),e.pixelStorei(X,1),e.pixelStorei(D,t.width),e.pixelStorei(N,0),e.pixelStorei(V,0),e.texImage3D(o,u,f,h,h,h,0,v,b,null);for(var R=0;R=0?w(n,r):t.indexOf("tan")>=0||t.indexOf("binorm")>=0?p(n,r):t.indexOf("norm")>=0&&x(n,r)})),e}function F(e,r,t){return e=e||2,{position:{numComponents:2,data:[(r=r||0)+-1*(e*=.5),(t=t||0)+-1*e,r+1*e,t+-1*e,r+-1*e,t+1*e,r+1*e,t+1*e]},normal:[0,0,1,0,0,1,0,0,1,0,0,1],texcoord:[0,0,1,0,0,1,1,1],indices:[0,1,2,2,1,3]}}function E(e,r,t,n,o){e=e||1,r=r||1,t=t||1,n=n||1,o=o||i.identity();for(var u=(t+1)*(n+1),a=b(3,u),f=b(3,u),c=b(2,u),l=0;l<=n;l++)for(var s=0;s<=t;s++){var v=s/t,y=l/n;a.push(e*v-.5*e,0,r*y-.5*r),f.push(0,1,0),c.push(v,y)}for(var d=t+1,m=b(3,t*n*2,Uint16Array),p=0;p 0");n=n||0,u=u||0;for(var a=(o=o||Math.PI)-n,f=(i=i||2*Math.PI)-u,c=(r+1)*(t+1),l=b(3,c),s=b(3,c),v=b(2,c),y=0;y<=t;y++)for(var d=0;d<=r;d++){var m=d/r,p=y/t,x=f*m+u,w=a*p+n,h=Math.sin(x),F=Math.cos(x),E=Math.sin(w),A=F*E,O=Math.cos(w),_=h*E;l.push(e*A,e*O,e*_),s.push(A,O,_),v.push(1-m,p)}for(var j=r+1,M=b(3,r*t*2,Uint16Array),P=0;Po?(A=t,E=1,O=r):O=e+F/o*(r-e),-2!==F&&F!==o+2||(O=0,E=0),A-=t/2;for(var _=0;_o?0:j*x,F<0?-1:F>o?1:w,F<0||F>o?0:M*x),y.push(_/n,1-E)}}for(var P=0;P 0");var f=2,c=(i=i||1)-(u=u||0),l=2*(o+1)*(2+f),s=b(3,l),v=b(3,l),y=b(2,l);function d(e,r,t){return e+(r-e)*t}function m(r,t,i,l,b,m){for(var p=0;p<=o;p++){var x=t/(f-1),w=p/o,h=2*(x-.5),F=(u+w*c)*Math.PI,E=Math.sin(F),A=Math.cos(F),O=d(e,r,E),_=h*n,j=A*e,M=E*O;s.push(_,j,M);var P=a.add(a.multiply([0,E,A],i),l);v.push(P),y.push(x*b+m,w)}}for(var p=0;p0&&m!==r){var h=l+(m+1),F=l+m,E=l+m-v,A=l+(m+1)-v;c.push(h,F,E),c.push(h,E,A)}}l+=r+1}return{position:i,normal:a,texcoord:f,indices:c}}function U(e){return function(r){var t=e.apply(this,Array.prototype.slice.call(arguments,1));return n.createBuffersFromArrays(r,t)}}function C(e){return function(r){var t=e.apply(null,Array.prototype.slice.call(arguments,1));return n.createBufferInfoFromArrays(r,t)}}var S=["numComponents","size","type","normalize","stride","offset","attrib","name","attribName"];function k(e,r,t,n){n=n||0;for(var o=e.length,u=0;u 2 | 3 | 4 | 5 | 6 | 7 | 17 | 18 | 19 | 20 | 21 | 104 | 105 | -------------------------------------------------------------------------------- /examples/unity/Build/UnityHTML.data.unityweb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/Build/UnityHTML.data.unityweb -------------------------------------------------------------------------------- /examples/unity/Build/UnityHTML.json: -------------------------------------------------------------------------------- 1 | { 2 | "companyName": "DefaultCompany", 3 | "productName": "delme-lwrp", 4 | "productVersion": "0.1", 5 | "dataUrl": "UnityHTML.data.unityweb", 6 | "wasmCodeUrl": "UnityHTML.wasm.code.unityweb", 7 | "wasmFrameworkUrl": "UnityHTML.wasm.framework.unityweb", 8 | "graphicsAPI": ["WebGL 2.0"], 9 | "webglContextAttributes": {"preserveDrawingBuffer": false}, 10 | "splashScreenStyle": "Dark", 11 | "backgroundColor": "#231F20", 12 | "cacheControl": {"default": "must-revalidate"}, 13 | "developmentBuild": false, 14 | "multithreading": false, 15 | "unityVersion": "2019.2.11f1" 16 | } -------------------------------------------------------------------------------- /examples/unity/Build/UnityHTML.wasm.code.unityweb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/Build/UnityHTML.wasm.code.unityweb -------------------------------------------------------------------------------- /examples/unity/Build/UnityHTML.wasm.framework.unityweb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/Build/UnityHTML.wasm.framework.unityweb -------------------------------------------------------------------------------- /examples/unity/TemplateData/UnityProgress.js: -------------------------------------------------------------------------------- 1 | function UnityProgress(unityInstance, progress) { 2 | if (!unityInstance.Module) 3 | return; 4 | if (!unityInstance.logo) { 5 | unityInstance.logo = document.createElement("div"); 6 | unityInstance.logo.className = "logo " + unityInstance.Module.splashScreenStyle; 7 | unityInstance.container.appendChild(unityInstance.logo); 8 | } 9 | if (!unityInstance.progress) { 10 | unityInstance.progress = document.createElement("div"); 11 | unityInstance.progress.className = "progress " + unityInstance.Module.splashScreenStyle; 12 | unityInstance.progress.empty = document.createElement("div"); 13 | unityInstance.progress.empty.className = "empty"; 14 | unityInstance.progress.appendChild(unityInstance.progress.empty); 15 | unityInstance.progress.full = document.createElement("div"); 16 | unityInstance.progress.full.className = "full"; 17 | unityInstance.progress.appendChild(unityInstance.progress.full); 18 | unityInstance.container.appendChild(unityInstance.progress); 19 | } 20 | unityInstance.progress.full.style.width = (100 * progress) + "%"; 21 | unityInstance.progress.empty.style.width = (100 * (1 - progress)) + "%"; 22 | if (progress == 1) 23 | unityInstance.logo.style.display = unityInstance.progress.style.display = "none"; 24 | } -------------------------------------------------------------------------------- /examples/unity/TemplateData/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/favicon.ico -------------------------------------------------------------------------------- /examples/unity/TemplateData/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/fullscreen.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressEmpty.Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressEmpty.Dark.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressEmpty.Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressEmpty.Light.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressFull.Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressFull.Dark.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressFull.Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressFull.Light.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressLogo.Dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressLogo.Dark.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/progressLogo.Light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/progressLogo.Light.png -------------------------------------------------------------------------------- /examples/unity/TemplateData/style.css: -------------------------------------------------------------------------------- 1 | .webgl-content * {border: 0; margin: 0; padding: 0} 2 | .webgl-content {position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);} 3 | 4 | .webgl-content .logo, .progress {position: absolute; left: 50%; top: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%);} 5 | .webgl-content .logo {background: url('progressLogo.Light.png') no-repeat center / contain; width: 154px; height: 130px;} 6 | .webgl-content .progress {height: 18px; width: 141px; margin-top: 90px;} 7 | .webgl-content .progress .empty {background: url('progressEmpty.Light.png') no-repeat right / cover; float: right; width: 100%; height: 100%; display: inline-block;} 8 | .webgl-content .progress .full {background: url('progressFull.Light.png') no-repeat left / cover; float: left; width: 0%; height: 100%; display: inline-block;} 9 | 10 | .webgl-content .logo.Dark {background-image: url('progressLogo.Dark.png');} 11 | .webgl-content .progress.Dark .empty {background-image: url('progressEmpty.Dark.png');} 12 | .webgl-content .progress.Dark .full {background-image: url('progressFull.Dark.png');} 13 | 14 | .webgl-content .footer {margin-top: 5px; height: 38px; line-height: 38px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 18px;} 15 | .webgl-content .footer .webgl-logo, .title, .fullscreen {height: 100%; display: inline-block; background: transparent center no-repeat;} 16 | .webgl-content .footer .webgl-logo {background-image: url('webgl-logo.png'); width: 204px; float: left;} 17 | .webgl-content .footer .title {margin-right: 10px; float: right;} 18 | .webgl-content .footer .fullscreen {background-image: url('fullscreen.png'); width: 38px; float: right;} 19 | -------------------------------------------------------------------------------- /examples/unity/TemplateData/webgl-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/examples/unity/TemplateData/webgl-logo.png -------------------------------------------------------------------------------- /examples/unity/index-log-shaders-webgl1-by-disabling-webgl2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Unity WebGL Player | cube 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 |
19 |
20 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/unity/index-log-shaders.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Unity WebGL Player | cube 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 |
19 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/unity/index-webgl-show-info.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unity WebGL Player | cube 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 |
18 |
19 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/unity/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Unity WebGL Player | cube 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 | 23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /images/dump-shaders-google-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/dump-shaders-google-maps.png -------------------------------------------------------------------------------- /images/log-draw-calls-jsconsole.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/log-draw-calls-jsconsole.gif -------------------------------------------------------------------------------- /images/log-draw-calls.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/log-draw-calls.html -------------------------------------------------------------------------------- /images/preservedrawingbuffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/preservedrawingbuffer.png -------------------------------------------------------------------------------- /images/threejs-log-shaders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/threejs-log-shaders.png -------------------------------------------------------------------------------- /images/unity-log-shaders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/unity-log-shaders.png -------------------------------------------------------------------------------- /images/webgl-show-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greggman/webgl-helpers/06e3d45373af0f3ce8f58b472b61835d9678d23a/images/webgl-show-info.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgl-helpers", 3 | "version": "1.8.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "chalk": { 34 | "version": "2.4.2", 35 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 36 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 37 | "dev": true, 38 | "requires": { 39 | "ansi-styles": "^3.2.1", 40 | "escape-string-regexp": "^1.0.5", 41 | "supports-color": "^5.3.0" 42 | } 43 | } 44 | } 45 | }, 46 | "@types/color-name": { 47 | "version": "1.1.1", 48 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 49 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", 50 | "dev": true 51 | }, 52 | "acorn": { 53 | "version": "7.3.1", 54 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", 55 | "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", 56 | "dev": true 57 | }, 58 | "acorn-jsx": { 59 | "version": "5.2.0", 60 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", 61 | "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 62 | "dev": true 63 | }, 64 | "ajv": { 65 | "version": "6.12.3", 66 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", 67 | "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", 68 | "dev": true, 69 | "requires": { 70 | "fast-deep-equal": "^3.1.1", 71 | "fast-json-stable-stringify": "^2.0.0", 72 | "json-schema-traverse": "^0.4.1", 73 | "uri-js": "^4.2.2" 74 | } 75 | }, 76 | "ansi-colors": { 77 | "version": "4.1.1", 78 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 79 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 80 | "dev": true 81 | }, 82 | "ansi-regex": { 83 | "version": "5.0.0", 84 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 85 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 86 | "dev": true 87 | }, 88 | "ansi-styles": { 89 | "version": "3.2.1", 90 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 91 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 92 | "dev": true, 93 | "requires": { 94 | "color-convert": "^1.9.0" 95 | } 96 | }, 97 | "argparse": { 98 | "version": "1.0.10", 99 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 100 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 101 | "dev": true, 102 | "requires": { 103 | "sprintf-js": "~1.0.2" 104 | } 105 | }, 106 | "astral-regex": { 107 | "version": "1.0.0", 108 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 109 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 110 | "dev": true 111 | }, 112 | "balanced-match": { 113 | "version": "1.0.0", 114 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 115 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 116 | "dev": true 117 | }, 118 | "brace-expansion": { 119 | "version": "1.1.11", 120 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 121 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 122 | "dev": true, 123 | "requires": { 124 | "balanced-match": "^1.0.0", 125 | "concat-map": "0.0.1" 126 | } 127 | }, 128 | "callsites": { 129 | "version": "3.1.0", 130 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 131 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 132 | "dev": true 133 | }, 134 | "chalk": { 135 | "version": "4.1.0", 136 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 137 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 138 | "dev": true, 139 | "requires": { 140 | "ansi-styles": "^4.1.0", 141 | "supports-color": "^7.1.0" 142 | }, 143 | "dependencies": { 144 | "ansi-styles": { 145 | "version": "4.2.1", 146 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 147 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 148 | "dev": true, 149 | "requires": { 150 | "@types/color-name": "^1.1.1", 151 | "color-convert": "^2.0.1" 152 | } 153 | }, 154 | "color-convert": { 155 | "version": "2.0.1", 156 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 157 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 158 | "dev": true, 159 | "requires": { 160 | "color-name": "~1.1.4" 161 | } 162 | }, 163 | "color-name": { 164 | "version": "1.1.4", 165 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 166 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 167 | "dev": true 168 | }, 169 | "has-flag": { 170 | "version": "4.0.0", 171 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 172 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 173 | "dev": true 174 | }, 175 | "supports-color": { 176 | "version": "7.1.0", 177 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 178 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 179 | "dev": true, 180 | "requires": { 181 | "has-flag": "^4.0.0" 182 | } 183 | } 184 | } 185 | }, 186 | "color-convert": { 187 | "version": "1.9.3", 188 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 189 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 190 | "dev": true, 191 | "requires": { 192 | "color-name": "1.1.3" 193 | } 194 | }, 195 | "color-name": { 196 | "version": "1.1.3", 197 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 198 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 199 | "dev": true 200 | }, 201 | "concat-map": { 202 | "version": "0.0.1", 203 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 204 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 205 | "dev": true 206 | }, 207 | "cross-spawn": { 208 | "version": "7.0.3", 209 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 210 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 211 | "dev": true, 212 | "requires": { 213 | "path-key": "^3.1.0", 214 | "shebang-command": "^2.0.0", 215 | "which": "^2.0.1" 216 | } 217 | }, 218 | "debug": { 219 | "version": "4.1.1", 220 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 221 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 222 | "dev": true, 223 | "requires": { 224 | "ms": "^2.1.1" 225 | } 226 | }, 227 | "deep-is": { 228 | "version": "0.1.3", 229 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 230 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 231 | "dev": true 232 | }, 233 | "doctrine": { 234 | "version": "3.0.0", 235 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 236 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 237 | "dev": true, 238 | "requires": { 239 | "esutils": "^2.0.2" 240 | } 241 | }, 242 | "emoji-regex": { 243 | "version": "7.0.3", 244 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 245 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 246 | "dev": true 247 | }, 248 | "enquirer": { 249 | "version": "2.3.6", 250 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 251 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 252 | "dev": true, 253 | "requires": { 254 | "ansi-colors": "^4.1.1" 255 | } 256 | }, 257 | "escape-string-regexp": { 258 | "version": "1.0.5", 259 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 260 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 261 | "dev": true 262 | }, 263 | "eslint": { 264 | "version": "7.5.0", 265 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.5.0.tgz", 266 | "integrity": "sha512-vlUP10xse9sWt9SGRtcr1LAC67BENcQMFeV+w5EvLEoFe3xJ8cF1Skd0msziRx/VMC+72B4DxreCE+OR12OA6Q==", 267 | "dev": true, 268 | "requires": { 269 | "@babel/code-frame": "^7.0.0", 270 | "ajv": "^6.10.0", 271 | "chalk": "^4.0.0", 272 | "cross-spawn": "^7.0.2", 273 | "debug": "^4.0.1", 274 | "doctrine": "^3.0.0", 275 | "enquirer": "^2.3.5", 276 | "eslint-scope": "^5.1.0", 277 | "eslint-utils": "^2.1.0", 278 | "eslint-visitor-keys": "^1.3.0", 279 | "espree": "^7.2.0", 280 | "esquery": "^1.2.0", 281 | "esutils": "^2.0.2", 282 | "file-entry-cache": "^5.0.1", 283 | "functional-red-black-tree": "^1.0.1", 284 | "glob-parent": "^5.0.0", 285 | "globals": "^12.1.0", 286 | "ignore": "^4.0.6", 287 | "import-fresh": "^3.0.0", 288 | "imurmurhash": "^0.1.4", 289 | "is-glob": "^4.0.0", 290 | "js-yaml": "^3.13.1", 291 | "json-stable-stringify-without-jsonify": "^1.0.1", 292 | "levn": "^0.4.1", 293 | "lodash": "^4.17.19", 294 | "minimatch": "^3.0.4", 295 | "natural-compare": "^1.4.0", 296 | "optionator": "^0.9.1", 297 | "progress": "^2.0.0", 298 | "regexpp": "^3.1.0", 299 | "semver": "^7.2.1", 300 | "strip-ansi": "^6.0.0", 301 | "strip-json-comments": "^3.1.0", 302 | "table": "^5.2.3", 303 | "text-table": "^0.2.0", 304 | "v8-compile-cache": "^2.0.3" 305 | } 306 | }, 307 | "eslint-scope": { 308 | "version": "5.1.0", 309 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", 310 | "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", 311 | "dev": true, 312 | "requires": { 313 | "esrecurse": "^4.1.0", 314 | "estraverse": "^4.1.1" 315 | } 316 | }, 317 | "eslint-utils": { 318 | "version": "2.1.0", 319 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 320 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 321 | "dev": true, 322 | "requires": { 323 | "eslint-visitor-keys": "^1.1.0" 324 | } 325 | }, 326 | "eslint-visitor-keys": { 327 | "version": "1.3.0", 328 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 329 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 330 | "dev": true 331 | }, 332 | "espree": { 333 | "version": "7.2.0", 334 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", 335 | "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", 336 | "dev": true, 337 | "requires": { 338 | "acorn": "^7.3.1", 339 | "acorn-jsx": "^5.2.0", 340 | "eslint-visitor-keys": "^1.3.0" 341 | } 342 | }, 343 | "esprima": { 344 | "version": "4.0.1", 345 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 346 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 347 | "dev": true 348 | }, 349 | "esquery": { 350 | "version": "1.3.1", 351 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", 352 | "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", 353 | "dev": true, 354 | "requires": { 355 | "estraverse": "^5.1.0" 356 | }, 357 | "dependencies": { 358 | "estraverse": { 359 | "version": "5.1.0", 360 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", 361 | "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", 362 | "dev": true 363 | } 364 | } 365 | }, 366 | "esrecurse": { 367 | "version": "4.2.1", 368 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 369 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 370 | "dev": true, 371 | "requires": { 372 | "estraverse": "^4.1.0" 373 | } 374 | }, 375 | "estraverse": { 376 | "version": "4.3.0", 377 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 378 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 379 | "dev": true 380 | }, 381 | "esutils": { 382 | "version": "2.0.3", 383 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 384 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 385 | "dev": true 386 | }, 387 | "fast-deep-equal": { 388 | "version": "3.1.3", 389 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 390 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 391 | "dev": true 392 | }, 393 | "fast-json-stable-stringify": { 394 | "version": "2.1.0", 395 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 396 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 397 | "dev": true 398 | }, 399 | "fast-levenshtein": { 400 | "version": "2.0.6", 401 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 402 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 403 | "dev": true 404 | }, 405 | "file-entry-cache": { 406 | "version": "5.0.1", 407 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 408 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 409 | "dev": true, 410 | "requires": { 411 | "flat-cache": "^2.0.1" 412 | } 413 | }, 414 | "flat-cache": { 415 | "version": "2.0.1", 416 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 417 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 418 | "dev": true, 419 | "requires": { 420 | "flatted": "^2.0.0", 421 | "rimraf": "2.6.3", 422 | "write": "1.0.3" 423 | } 424 | }, 425 | "flatted": { 426 | "version": "2.0.2", 427 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 428 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 429 | "dev": true 430 | }, 431 | "fs.realpath": { 432 | "version": "1.0.0", 433 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 434 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 435 | "dev": true 436 | }, 437 | "functional-red-black-tree": { 438 | "version": "1.0.1", 439 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 440 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 441 | "dev": true 442 | }, 443 | "glob": { 444 | "version": "7.1.6", 445 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 446 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 447 | "dev": true, 448 | "requires": { 449 | "fs.realpath": "^1.0.0", 450 | "inflight": "^1.0.4", 451 | "inherits": "2", 452 | "minimatch": "^3.0.4", 453 | "once": "^1.3.0", 454 | "path-is-absolute": "^1.0.0" 455 | } 456 | }, 457 | "glob-parent": { 458 | "version": "5.1.1", 459 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 460 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 461 | "dev": true, 462 | "requires": { 463 | "is-glob": "^4.0.1" 464 | } 465 | }, 466 | "globals": { 467 | "version": "12.4.0", 468 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 469 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 470 | "dev": true, 471 | "requires": { 472 | "type-fest": "^0.8.1" 473 | } 474 | }, 475 | "has-flag": { 476 | "version": "3.0.0", 477 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 478 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 479 | "dev": true 480 | }, 481 | "ignore": { 482 | "version": "4.0.6", 483 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 484 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 485 | "dev": true 486 | }, 487 | "import-fresh": { 488 | "version": "3.2.1", 489 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 490 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 491 | "dev": true, 492 | "requires": { 493 | "parent-module": "^1.0.0", 494 | "resolve-from": "^4.0.0" 495 | } 496 | }, 497 | "imurmurhash": { 498 | "version": "0.1.4", 499 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 500 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 501 | "dev": true 502 | }, 503 | "inflight": { 504 | "version": "1.0.6", 505 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 506 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 507 | "dev": true, 508 | "requires": { 509 | "once": "^1.3.0", 510 | "wrappy": "1" 511 | } 512 | }, 513 | "inherits": { 514 | "version": "2.0.4", 515 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 516 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 517 | "dev": true 518 | }, 519 | "is-extglob": { 520 | "version": "2.1.1", 521 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 522 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 523 | "dev": true 524 | }, 525 | "is-fullwidth-code-point": { 526 | "version": "2.0.0", 527 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 528 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 529 | "dev": true 530 | }, 531 | "is-glob": { 532 | "version": "4.0.1", 533 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 534 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 535 | "dev": true, 536 | "requires": { 537 | "is-extglob": "^2.1.1" 538 | } 539 | }, 540 | "isexe": { 541 | "version": "2.0.0", 542 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 543 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 544 | "dev": true 545 | }, 546 | "js-tokens": { 547 | "version": "4.0.0", 548 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 549 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 550 | "dev": true 551 | }, 552 | "js-yaml": { 553 | "version": "3.14.0", 554 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 555 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 556 | "dev": true, 557 | "requires": { 558 | "argparse": "^1.0.7", 559 | "esprima": "^4.0.0" 560 | } 561 | }, 562 | "json-schema-traverse": { 563 | "version": "0.4.1", 564 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 565 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 566 | "dev": true 567 | }, 568 | "json-stable-stringify-without-jsonify": { 569 | "version": "1.0.1", 570 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 571 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 572 | "dev": true 573 | }, 574 | "levn": { 575 | "version": "0.4.1", 576 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 577 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 578 | "dev": true, 579 | "requires": { 580 | "prelude-ls": "^1.2.1", 581 | "type-check": "~0.4.0" 582 | } 583 | }, 584 | "lodash": { 585 | "version": "4.17.19", 586 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 587 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", 588 | "dev": true 589 | }, 590 | "minimatch": { 591 | "version": "3.0.4", 592 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 593 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 594 | "dev": true, 595 | "requires": { 596 | "brace-expansion": "^1.1.7" 597 | } 598 | }, 599 | "minimist": { 600 | "version": "1.2.5", 601 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 602 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 603 | "dev": true 604 | }, 605 | "mkdirp": { 606 | "version": "0.5.5", 607 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 608 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 609 | "dev": true, 610 | "requires": { 611 | "minimist": "^1.2.5" 612 | } 613 | }, 614 | "ms": { 615 | "version": "2.1.2", 616 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 617 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 618 | "dev": true 619 | }, 620 | "natural-compare": { 621 | "version": "1.4.0", 622 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 623 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 624 | "dev": true 625 | }, 626 | "once": { 627 | "version": "1.4.0", 628 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 629 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 630 | "dev": true, 631 | "requires": { 632 | "wrappy": "1" 633 | } 634 | }, 635 | "optionator": { 636 | "version": "0.9.1", 637 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 638 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 639 | "dev": true, 640 | "requires": { 641 | "deep-is": "^0.1.3", 642 | "fast-levenshtein": "^2.0.6", 643 | "levn": "^0.4.1", 644 | "prelude-ls": "^1.2.1", 645 | "type-check": "^0.4.0", 646 | "word-wrap": "^1.2.3" 647 | } 648 | }, 649 | "parent-module": { 650 | "version": "1.0.1", 651 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 652 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 653 | "dev": true, 654 | "requires": { 655 | "callsites": "^3.0.0" 656 | } 657 | }, 658 | "path-is-absolute": { 659 | "version": "1.0.1", 660 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 661 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 662 | "dev": true 663 | }, 664 | "path-key": { 665 | "version": "3.1.1", 666 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 667 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 668 | "dev": true 669 | }, 670 | "prelude-ls": { 671 | "version": "1.2.1", 672 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 673 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 674 | "dev": true 675 | }, 676 | "progress": { 677 | "version": "2.0.3", 678 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 679 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 680 | "dev": true 681 | }, 682 | "punycode": { 683 | "version": "2.1.1", 684 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 685 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 686 | "dev": true 687 | }, 688 | "regexpp": { 689 | "version": "3.1.0", 690 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 691 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 692 | "dev": true 693 | }, 694 | "resolve-from": { 695 | "version": "4.0.0", 696 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 697 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 698 | "dev": true 699 | }, 700 | "rimraf": { 701 | "version": "2.6.3", 702 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 703 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 704 | "dev": true, 705 | "requires": { 706 | "glob": "^7.1.3" 707 | } 708 | }, 709 | "semver": { 710 | "version": "7.3.2", 711 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 712 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 713 | "dev": true 714 | }, 715 | "shebang-command": { 716 | "version": "2.0.0", 717 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 718 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 719 | "dev": true, 720 | "requires": { 721 | "shebang-regex": "^3.0.0" 722 | } 723 | }, 724 | "shebang-regex": { 725 | "version": "3.0.0", 726 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 727 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 728 | "dev": true 729 | }, 730 | "slice-ansi": { 731 | "version": "2.1.0", 732 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 733 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 734 | "dev": true, 735 | "requires": { 736 | "ansi-styles": "^3.2.0", 737 | "astral-regex": "^1.0.0", 738 | "is-fullwidth-code-point": "^2.0.0" 739 | } 740 | }, 741 | "sprintf-js": { 742 | "version": "1.0.3", 743 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 744 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 745 | "dev": true 746 | }, 747 | "string-width": { 748 | "version": "3.1.0", 749 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 750 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 751 | "dev": true, 752 | "requires": { 753 | "emoji-regex": "^7.0.1", 754 | "is-fullwidth-code-point": "^2.0.0", 755 | "strip-ansi": "^5.1.0" 756 | }, 757 | "dependencies": { 758 | "ansi-regex": { 759 | "version": "4.1.0", 760 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 761 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 762 | "dev": true 763 | }, 764 | "strip-ansi": { 765 | "version": "5.2.0", 766 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 767 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 768 | "dev": true, 769 | "requires": { 770 | "ansi-regex": "^4.1.0" 771 | } 772 | } 773 | } 774 | }, 775 | "strip-ansi": { 776 | "version": "6.0.0", 777 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 778 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 779 | "dev": true, 780 | "requires": { 781 | "ansi-regex": "^5.0.0" 782 | } 783 | }, 784 | "strip-json-comments": { 785 | "version": "3.1.1", 786 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 787 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 788 | "dev": true 789 | }, 790 | "supports-color": { 791 | "version": "5.5.0", 792 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 793 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 794 | "dev": true, 795 | "requires": { 796 | "has-flag": "^3.0.0" 797 | } 798 | }, 799 | "table": { 800 | "version": "5.4.6", 801 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 802 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 803 | "dev": true, 804 | "requires": { 805 | "ajv": "^6.10.2", 806 | "lodash": "^4.17.14", 807 | "slice-ansi": "^2.1.0", 808 | "string-width": "^3.0.0" 809 | } 810 | }, 811 | "text-table": { 812 | "version": "0.2.0", 813 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 814 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 815 | "dev": true 816 | }, 817 | "type-check": { 818 | "version": "0.4.0", 819 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 820 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 821 | "dev": true, 822 | "requires": { 823 | "prelude-ls": "^1.2.1" 824 | } 825 | }, 826 | "type-fest": { 827 | "version": "0.8.1", 828 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 829 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 830 | "dev": true 831 | }, 832 | "uri-js": { 833 | "version": "4.2.2", 834 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 835 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 836 | "dev": true, 837 | "requires": { 838 | "punycode": "^2.1.0" 839 | } 840 | }, 841 | "v8-compile-cache": { 842 | "version": "2.1.1", 843 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 844 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 845 | "dev": true 846 | }, 847 | "which": { 848 | "version": "2.0.2", 849 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 850 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 851 | "dev": true, 852 | "requires": { 853 | "isexe": "^2.0.0" 854 | } 855 | }, 856 | "word-wrap": { 857 | "version": "1.2.3", 858 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 859 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 860 | "dev": true 861 | }, 862 | "wrappy": { 863 | "version": "1.0.2", 864 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 865 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 866 | "dev": true 867 | }, 868 | "write": { 869 | "version": "1.0.3", 870 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 871 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 872 | "dev": true, 873 | "requires": { 874 | "mkdirp": "^0.5.1" 875 | } 876 | } 877 | } 878 | } 879 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgl-helpers", 3 | "version": "1.8.0", 4 | "description": "Some helper scripts for WebGL", 5 | "main": "dist/webgl-helpers.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/greggman/webgl-helpers.git" 12 | }, 13 | "keywords": [ 14 | "webgl" 15 | ], 16 | "author": "Gregg Tavares", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/greggman/webgl-helpers/issues" 20 | }, 21 | "homepage": "https://github.com/greggman/webgl-helpers#readme", 22 | "devDependencies": { 23 | "eslint": "^7.5.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webgl-check-framebuffer-feedback.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Note: This code is could be faster if we cached 3 | * a ton of stuff but it would make it far more complicated 4 | * and you should only be using this occasionally to help 5 | * find your error. 6 | */ 7 | (function() { 8 | function glEnumToString(gl, value) { 9 | const keys = []; 10 | for (const key in gl) { 11 | if (gl[key] === value) { 12 | keys.push(key); 13 | } 14 | } 15 | return keys.length ? keys.join(' | ') : `0x${value.toString(16)}`; 16 | } 17 | 18 | function isBuiltIn(name) { 19 | return name.startsWith("gl_") || name.startsWith("webgl_"); 20 | } 21 | 22 | function isWebGL2(gl) { 23 | // a proxy for if this is webgl 24 | return !!gl.texImage3D; 25 | } 26 | 27 | const extensionToContext = new Map(); 28 | function rememberExtensionContext(gl, ext) { 29 | extensionToContext.set(ext, gl); 30 | } 31 | 32 | const SAMPLER_2D = 0x8B5E; 33 | const SAMPLER_CUBE = 0x8B60; 34 | const SAMPLER_3D = 0x8B5F; 35 | const SAMPLER_2D_SHADOW = 0x8B62; 36 | const SAMPLER_2D_ARRAY = 0x8DC1; 37 | const SAMPLER_2D_ARRAY_SHADOW = 0x8DC4; 38 | const SAMPLER_CUBE_SHADOW = 0x8DC5; 39 | const samplers = new Set([ 40 | SAMPLER_2D, 41 | SAMPLER_CUBE, 42 | SAMPLER_3D, 43 | SAMPLER_2D_SHADOW, 44 | SAMPLER_2D_ARRAY, 45 | SAMPLER_2D_ARRAY_SHADOW, 46 | SAMPLER_CUBE_SHADOW, 47 | ]); 48 | 49 | function isSampler(type) { 50 | return samplers.has(type); 51 | } 52 | 53 | const TEXTURE_BINDING_2D = 0x8069; 54 | const TEXTURE_BINDING_CUBE_MAP = 0x8514; 55 | const TEXTURE_BINDING_3D = 0x806A; 56 | const TEXTURE_BINDING_2D_ARRAY = 0x8C1D; 57 | 58 | const samplerTypeToBinding = new Map(); 59 | samplerTypeToBinding.set(SAMPLER_2D, TEXTURE_BINDING_2D); 60 | samplerTypeToBinding.set(SAMPLER_2D_SHADOW, TEXTURE_BINDING_2D); 61 | samplerTypeToBinding.set(SAMPLER_3D, TEXTURE_BINDING_3D); 62 | samplerTypeToBinding.set(SAMPLER_2D_ARRAY, TEXTURE_BINDING_2D_ARRAY); 63 | samplerTypeToBinding.set(SAMPLER_2D_ARRAY_SHADOW, TEXTURE_BINDING_2D_ARRAY); 64 | samplerTypeToBinding.set(SAMPLER_CUBE, TEXTURE_BINDING_CUBE_MAP); 65 | samplerTypeToBinding.set(SAMPLER_CUBE_SHADOW, TEXTURE_BINDING_CUBE_MAP); 66 | 67 | function getTextureForUnit(gl, unit, type) { 68 | gl.activeTexture(gl.TEXTURE0 + unit); 69 | const binding = samplerTypeToBinding.get(type); 70 | return gl.getParameter(binding); 71 | } 72 | 73 | /** 74 | * slow non-cached version 75 | * @param {WebGLRenderingContext} gl 76 | * @param {number} attachment 77 | * @param {Map} textureAttachments 78 | */ 79 | function addTextureAttachment(gl, attachment, textureAttachments) { 80 | const type = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); 81 | if (type === gl.NONE) { 82 | return; 83 | } 84 | const obj = gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME); 85 | if (obj instanceof WebGLTexture) { 86 | if (!textureAttachments.has(obj)) { 87 | textureAttachments.set(obj, []); 88 | } 89 | textureAttachments.get(obj).push(attachment); 90 | } 91 | } 92 | 93 | const MAX_COLOR_ATTACHMENTS = 0x8CDF; 94 | 95 | function getMaxColorAttachments(gl) { 96 | if (!isWebGL2(gl)) { 97 | const ext = gl.getExtension('WEBGL_draw_buffers'); 98 | if (!ext) { 99 | return 1; 100 | } 101 | } 102 | return gl.getParameter(MAX_COLOR_ATTACHMENTS); 103 | } 104 | 105 | /** 106 | * slow non-cached version 107 | * @param {WebGLRenderingContext} gl 108 | */ 109 | function checkFramebufferFeedback(gl) { 110 | const framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); 111 | if (!framebuffer) { 112 | // drawing to canvas 113 | return; 114 | } 115 | 116 | // get framebuffer texture attachments 117 | const maxColorAttachments = getMaxColorAttachments(gl) 118 | const textureAttachments = new Map(); 119 | for (let i = 0; i < maxColorAttachments; ++i) { 120 | addTextureAttachment(gl, gl.COLOR_ATTACHMENT0 + i, textureAttachments); 121 | } 122 | addTextureAttachment(gl, gl.DEPTH_ATTACHMENT, textureAttachments); 123 | addTextureAttachment(gl, gl.STENCIL_ATTACHMENT, textureAttachments); 124 | 125 | if (!isWebGL2(gl)) { 126 | addTextureAttachment(gl, gl.DEPTH_STENCIL_ATTACHMENT, textureAttachments); 127 | } 128 | 129 | const oldActiveTexture = gl.getParameter(gl.ACTIVE_TEXTURE); 130 | const program = gl.getParameter(gl.CURRENT_PROGRAM); 131 | // get the texture units used by the current program 132 | const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 133 | const errors = []; 134 | for (let ii = 0; ii < numUniforms; ++ii) { 135 | const {name, type, size} = gl.getActiveUniform(program, ii); 136 | if (isBuiltIn(name) || !isSampler(type)) { 137 | continue; 138 | } 139 | 140 | if (size > 1) { 141 | let baseName = (name.substr(-3) === "[0]") 142 | ? name.substr(0, name.length - 3) 143 | : name; 144 | for (let t = 0; t < size; ++t) { 145 | errors.push(...checkTextureUsage(gl, textureAttachments, program, `${baseName}[${t}]`, type)); 146 | } 147 | } else { 148 | errors.push(...checkTextureUsage(gl, textureAttachments, program, name, type)); 149 | } 150 | } 151 | gl.activeTexture(oldActiveTexture); 152 | 153 | if (errors.length) { 154 | throw new Error(`WebGL feedback loop: ${errors.join('\n')}`); 155 | } 156 | } 157 | 158 | function checkFramebufferFeedbackExt(ext) { 159 | checkFramebufferFeedback(extensionToContext.get(ext)); 160 | } 161 | 162 | function checkTextureUsage(gl, textureAttachments, program, uniformName, uniformType) { 163 | const location = gl.getUniformLocation(program, uniformName); 164 | const textureUnit = gl.getUniform(program, location); 165 | const texture = getTextureForUnit(gl, textureUnit, uniformType); 166 | const attachments = textureAttachments.get(texture); 167 | return attachments 168 | ? [`texture on uniform: ${uniformName} bound to texture unit ${textureUnit} is also attached to current framebuffer on attachment: ${attachments.map(a => glEnumToString(gl, a)).join(', ')}`] 169 | : []; 170 | } 171 | 172 | function wrapFn(p, fn, wrapperFn) { 173 | const origFn = p[fn]; 174 | p[fn] = function(...args) { 175 | const result = origFn.call(this, ...args); 176 | wrapperFn(this, result, ...args); 177 | return result; 178 | }; 179 | } 180 | 181 | function wrapFnP(p, fn, wrapperFn) { 182 | wrapFn(p.prototype, fn, wrapperFn); 183 | } 184 | 185 | wrapFnP(WebGLRenderingContext, 'getExtension', rememberExtensionContext); 186 | wrapFnP(WebGLRenderingContext, 'drawArrays', checkFramebufferFeedback); 187 | wrapFnP(WebGLRenderingContext, 'drawElements', checkFramebufferFeedback); 188 | 189 | if (typeof WebGL2RenderingContext !== 'undefined') { 190 | wrapFnP(WebGL2RenderingContext, 'getExtension', rememberExtensionContext); 191 | wrapFnP(WebGL2RenderingContext, 'drawArrays', checkFramebufferFeedback); 192 | wrapFnP(WebGL2RenderingContext, 'drawElements', checkFramebufferFeedback); 193 | wrapFnP(WebGL2RenderingContext, 'drawArraysInstanced', checkFramebufferFeedback); 194 | wrapFnP(WebGL2RenderingContext, 'drawElementsInstanced', checkFramebufferFeedback); 195 | wrapFnP(WebGL2RenderingContext, 'drawRangeElements', checkFramebufferFeedback); 196 | } 197 | 198 | const ext = document.createElement("canvas").getContext("webgl").getExtension('ANGLE_instanced_arrays'); 199 | if (ext) { 200 | wrapFn(ext.__proto__, 'drawArraysInstancedANGLE', checkFramebufferFeedbackExt); 201 | wrapFn(ext.__proto__, 'drawElementsInstancedANGLE', checkFramebufferFeedbackExt); 202 | } 203 | }()) 204 | -------------------------------------------------------------------------------- /webgl-dump-shaders.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | const extensionToContext = new Map(); 3 | function rememberExtensionContext(gl, ext) { 4 | extensionToContext.set(ext, gl); 5 | } 6 | 7 | const dumped = new Set(); 8 | 9 | /** 10 | * @param {WebGLRenderingContext} gl 11 | * @param {WebGLProgram} program 12 | */ 13 | function invalidate(gl, program) { 14 | dumped.delete(program); 15 | } 16 | 17 | /** 18 | * @param {WebGLRenderingContext} gl 19 | */ 20 | function dumpShaders(gl) { 21 | const program = gl.getParameter(gl.CURRENT_PROGRAM); 22 | if (!program || dumped.has(program)) { 23 | return; 24 | } 25 | 26 | dumped.add(program); 27 | 28 | const shaders = gl.getAttachedShaders(program); 29 | for (const shader of shaders) { 30 | console.log(gl.getShaderSource(shader)); 31 | } 32 | } 33 | 34 | function dumpShadersExt(ext) { 35 | const gl = extensionToContext.get(ext); 36 | if (gl) { 37 | dumpShaders(gl); 38 | } 39 | } 40 | 41 | function checkForExtension(gl) { 42 | const ext = gl.getExtension('ANGLE_instanced_arrays'); 43 | extensionToContext.set(ext, gl); 44 | } 45 | 46 | function wrapFn(p, fn, wrapperFn) { 47 | const origFn = p[fn]; 48 | p[fn] = function(...args) { 49 | const result = origFn.call(this, ...args); 50 | wrapperFn(this, result, ...args); 51 | return result; 52 | }; 53 | } 54 | 55 | function wrapFnP(p, fn, wrapperFn) { 56 | wrapFn(p.prototype, fn, wrapperFn); 57 | } 58 | 59 | wrapFnP(WebGLRenderingContext, 'getExtension', rememberExtensionContext); 60 | wrapFnP(WebGLRenderingContext, 'linkProgram', invalidate); 61 | wrapFnP(WebGLRenderingContext, 'useProgram', checkForExtension); 62 | wrapFnP(WebGLRenderingContext, 'drawArrays', dumpShaders); 63 | wrapFnP(WebGLRenderingContext, 'drawElements', dumpShaders); 64 | 65 | if (typeof WebGL2RenderingContext !== 'undefined') { 66 | wrapFnP(WebGL2RenderingContext, 'getExtension', rememberExtensionContext); 67 | wrapFnP(WebGL2RenderingContext, 'linkProgram', invalidate); 68 | wrapFnP(WebGL2RenderingContext, 'drawArrays', dumpShaders); 69 | wrapFnP(WebGL2RenderingContext, 'drawElements', dumpShaders); 70 | wrapFnP(WebGL2RenderingContext, 'drawArraysInstanced', dumpShaders); 71 | wrapFnP(WebGL2RenderingContext, 'drawElementsInstanced', dumpShaders); 72 | wrapFnP(WebGL2RenderingContext, 'drawRangeElements', dumpShaders); 73 | } 74 | 75 | const ext = document.createElement("canvas").getContext("webgl").getExtension('ANGLE_instanced_arrays'); 76 | if (ext) { 77 | wrapFn(ext.__proto__, 'drawArraysInstancedANGLE', dumpShadersExt); 78 | wrapFn(ext.__proto__, 'drawElementsInstancedANGLE', dumpShadersExt); 79 | } 80 | 81 | }()); 82 | -------------------------------------------------------------------------------- /webgl-force-alpha-false.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {alpha: false}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-alpha-true.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {alpha: true}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-powerpreference-high-performance.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {powerPreference: "high-performance"}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-powerpreference-low-power.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {powerPreference: "low-power"}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-premultipliedalpha-false.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {premultipliedAlpha: false}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-premultipliedalpha-true.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {premultipliedAlpha: true}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-force-preservedrawingbuffer.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | const isWebGL = /webgl/i; 12 | 13 | ContextClass.prototype.getContext = function(origFn) { 14 | return function(type, attributes) { 15 | if (isWebGL.test(type)) { 16 | attributes = Object.assign({}, attributes || {}, {preserveDrawingBuffer: true}); 17 | } 18 | return origFn.call(this, type, attributes); 19 | }; 20 | }(ContextClass.prototype.getContext); 21 | } 22 | 23 | }()); 24 | -------------------------------------------------------------------------------- /webgl-gl-error-check.js: -------------------------------------------------------------------------------- 1 | console.log('webgl-gl-error-check.js has moved. See https://github.com/greggman/webgl-lint/'); 2 | -------------------------------------------------------------------------------- /webgl-log-bad-shaders.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof WebGLRenderingContext !== "undefined") { 4 | addShaderLogger(WebGLRenderingContext); 5 | addProgramLogger(WebGLRenderingContext); 6 | } 7 | if (typeof WebGL2RenderingContext !== "undefined") { 8 | addShaderLogger(WebGL2RenderingContext); 9 | addProgramLogger(WebGL2RenderingContext); 10 | } 11 | 12 | function addShaderLogger(ContextClass) { 13 | ContextClass.prototype.compileShader = function(origFn) { 14 | return function(shader) { 15 | origFn.apply(this, arguments); 16 | const result = this.getShaderParameter(shader, this.COMPILE_STATUS); 17 | if (!result) { 18 | console.error(`bad shader:\n${this.getShaderSource(shader)}`); 19 | } 20 | }; 21 | }(ContextClass.prototype.compileShader); 22 | } 23 | 24 | function addProgramLogger(ContextClass) { 25 | ContextClass.prototype.linkProgram = function(origFn) { 26 | return function(program) { 27 | origFn.apply(this, arguments); 28 | const result = this.getProgramParameter(program, this.LINK_STATUS); 29 | if (!result) { 30 | console.error(`bad link:\n${this.getAttachedShaders(program).map(shader => this.getShaderSource(shader)).join('\n')}`); 31 | } 32 | }; 33 | }(ContextClass.prototype.linkProgram); 34 | } 35 | 36 | }()); 37 | -------------------------------------------------------------------------------- /webgl-log-shaders.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof WebGLRenderingContext !== "undefined") { 4 | addShaderLogger(WebGLRenderingContext); 5 | } 6 | if (typeof WebGL2RenderingContext !== "undefined") { 7 | addShaderLogger(WebGL2RenderingContext); 8 | } 9 | 10 | function addShaderLogger(ContextClass) { 11 | ContextClass.prototype.shaderSource = function(origFn) { 12 | return function(shader, source) { 13 | console.log(source); 14 | return origFn.apply(this, arguments); 15 | }; 16 | }(ContextClass.prototype.shaderSource); 17 | } 18 | 19 | }()); 20 | -------------------------------------------------------------------------------- /webgl-show-draw-calls.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | function glEnumToString(gl, value) { 3 | const keys = []; 4 | for (const key in gl) { 5 | if (gl[key] === value) { 6 | keys.push(key); 7 | } 8 | } 9 | return keys.length ? keys.join(' | ') : `0x${value.toString(16)}`; 10 | } 11 | 12 | function wrapFn(p, fn) { 13 | const origFn = p[fn]; 14 | p[fn] = function(...args) { 15 | console.log(fn, glEnumToString(this, args[0])); 16 | return origFn.call(this, ...args); 17 | }; 18 | } 19 | 20 | function wrapFnP(p, fn) { 21 | wrapFn(p.prototype, fn); 22 | } 23 | 24 | wrapFnP(WebGLRenderingContext, 'drawArrays'); 25 | wrapFnP(WebGLRenderingContext, 'drawElements'); 26 | 27 | if (typeof WebGL2RenderingContext !== 'undefined') { 28 | wrapFnP(WebGL2RenderingContext, 'drawArrays'); 29 | wrapFnP(WebGL2RenderingContext, 'drawElements'); 30 | wrapFnP(WebGL2RenderingContext, 'drawArraysInstanced'); 31 | wrapFnP(WebGL2RenderingContext, 'drawElementsInstanced'); 32 | wrapFnP(WebGL2RenderingContext, 'drawRangeElements'); 33 | } 34 | 35 | const ext = document.createElement("canvas").getContext("webgl").getExtension('ANGLE_instanced_arrays'); 36 | if (ext) { 37 | wrapFn(ext.__proto__, 'drawArraysInstancedANGLE'); 38 | wrapFn(ext.__proto__, 'drawElementsInstancedANGLE'); 39 | } 40 | }()) 41 | -------------------------------------------------------------------------------- /webgl-show-info.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | const enumToStringMap = new Map(); 3 | { 4 | const api = WebGL2RenderingContext.prototype; 5 | for (const key of Object.keys(api)) { 6 | const desc = Object.getOwnPropertyDescriptor(api, key); 7 | if (desc.writable !== false) { 8 | continue; 9 | } 10 | const value = api[key]; 11 | if (typeof value === 'number') { 12 | enumToStringMap.set(value, key); 13 | } 14 | } 15 | } 16 | enumToStringMap.set(WebGL2RenderingContext.prototype.POINTS, 'POINTS'); // because POINTS = 0 >:( 17 | function enumToString(v) { 18 | return enumToStringMap.get(v) || v; 19 | } 20 | 21 | // I know ths is not a full check 22 | function isNumber(v) { 23 | return typeof v === 'number'; 24 | } 25 | 26 | /* PixelFormat */ 27 | const ALPHA = 0x1906; 28 | const RGB = 0x1907; 29 | const RGBA = 0x1908; 30 | const LUMINANCE = 0x1909; 31 | const LUMINANCE_ALPHA = 0x190A; 32 | const DEPTH_COMPONENT = 0x1902; 33 | const DEPTH_STENCIL = 0x84F9; 34 | 35 | 36 | /* DataType */ 37 | const BYTE = 0x1400; 38 | const UNSIGNED_BYTE = 0x1401; 39 | const SHORT = 0x1402; 40 | const UNSIGNED_SHORT = 0x1403; 41 | const INT = 0x1404; 42 | const UNSIGNED_INT = 0x1405; 43 | const FLOAT = 0x1406; 44 | const UNSIGNED_SHORT_4_4_4_4 = 0x8033; 45 | const UNSIGNED_SHORT_5_5_5_1 = 0x8034; 46 | const UNSIGNED_SHORT_5_6_5 = 0x8363; 47 | const HALF_FLOAT = 0x140B; 48 | const HALF_FLOAT_OES = 0x8D61; // Thanks Khronos for making this different >:( 49 | const UNSIGNED_INT_2_10_10_10_REV = 0x8368; 50 | const UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B; 51 | const UNSIGNED_INT_5_9_9_9_REV = 0x8C3E; 52 | const FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD; 53 | const UNSIGNED_INT_24_8 = 0x84FA; 54 | 55 | const RG = 0x8227; 56 | const RG_INTEGER = 0x8228; 57 | const RED = 0x1903; 58 | const RED_INTEGER = 0x8D94; 59 | const RGB_INTEGER = 0x8D98; 60 | const RGBA_INTEGER = 0x8D99; 61 | 62 | const ELEMENT_ARRAY_BUFFER = 0x8893; 63 | 64 | const formatToNumComponentsMap = new Map([ 65 | [ ALPHA , 1 ], 66 | [ LUMINANCE , 1 ], 67 | [ LUMINANCE_ALPHA , 2 ], 68 | [ RGB , 3 ], 69 | [ RGBA , 4 ], 70 | [ RED , 1 ], 71 | [ RED_INTEGER , 1 ], 72 | [ RG , 2 ], 73 | [ RG_INTEGER , 2 ], 74 | [ RGB , 3 ], 75 | [ RGB_INTEGER , 3 ], 76 | [ RGBA , 4 ], 77 | [ RGBA_INTEGER , 4 ], 78 | [ DEPTH_COMPONENT , 1 ], 79 | [ DEPTH_STENCIL , 2 ], 80 | ]); 81 | 82 | const glTypeToSizeMap = new Map([ 83 | [BYTE , 1], 84 | [UNSIGNED_BYTE , 1], 85 | [SHORT , 2], 86 | [UNSIGNED_SHORT , 2], 87 | [INT , 4], 88 | [UNSIGNED_INT , 4], 89 | [FLOAT , 4], 90 | [FLOAT , 4], 91 | [UNSIGNED_SHORT_4_4_4_4, 0.5], 92 | [UNSIGNED_SHORT_5_5_5_1, 0.5], 93 | [UNSIGNED_SHORT_5_6_5, 1/3], 94 | [HALF_FLOAT , 2], 95 | [HALF_FLOAT_OES , 2], 96 | [UNSIGNED_INT_2_10_10_10_REV, 1], 97 | [UNSIGNED_INT_10F_11F_11F_REV, 1], 98 | [UNSIGNED_INT_5_9_9_9_REV, 1], 99 | [FLOAT_32_UNSIGNED_INT_24_8_REV, 2], 100 | [UNSIGNED_INT_24_8, 2], 101 | ]); 102 | 103 | function computeBytesForFormatType(width, height, depth, format, type) { 104 | const elemSize = glTypeToSizeMap.get(type) * formatToNumComponentsMap.get(format); 105 | return width * height * depth * elemSize; 106 | } 107 | 108 | const targetToByteCountMap = new Map(); 109 | const readTargetToByteCountMap = new Map(); 110 | 111 | const texImage2DArgParsersMap = new Map([ 112 | [9, function([target, level, internalFormat, width, height, , format, type, src]) { 113 | return {target, level, internalFormat, width, height, format, type, src}; 114 | }, ], 115 | [6, function([target, level, internalFormat, format, type, texImageSource]) { 116 | return {target, level, internalFormat, width: texImageSource.width, height: texImageSource.height, format, type, src: true}; 117 | }, ], 118 | [10, function([target, level, internalFormat, width, height, , format, type]) { 119 | return {target, level, internalFormat, width, height, format, type, src: true}; 120 | }, ], 121 | ]); 122 | 123 | const texSubImage2DArgParsersMap = new Map([ 124 | [9, function([target, level, x, y, width, height, format, type, src]) { 125 | return {target, level, width, height, format, type, src: !isNumber(src)}; 126 | }, ], 127 | [7, function([target, level, x, y, width, height, format, type, texImageSource]) { 128 | return {target, level, width: texImageSource.width, height: texImageSource.height, format, type, src: true}; 129 | }, ], 130 | [10, function([target, level, x, y, width, height, format, type]) { 131 | return {target, level, width, height, format, type, src: true}; 132 | }, ], 133 | ]); 134 | 135 | const texImage3DArgParsersMap = new Map([ 136 | [10, function([target, level, internalFormat, width, height, depth, , format, type, src]) { 137 | return {target, level, internalFormat, width, height, format, depth, type, src: !isNumber(src)}; 138 | }, ], 139 | [11, function([target, level, internalFormat, width, height, depth, , format, type, src]) { 140 | return {target, level, internalFormat, width, height, depth, format, type, src: !isNumber(src)}; 141 | }, ], 142 | ]); 143 | 144 | const texSubImage3DArgParsersMap = new Map([ 145 | [11, function([target, level, x, y, z, width, height, depth, format, type, src]) { 146 | return {target, level, width, height, depth, format, type, src: !isNumber(src)}; 147 | }, ], 148 | [12, function([target, level, x, y, z, width, height, depth, format, type, src]) { 149 | return {target, level, width, height, depth, format, type, src: !isNumber(src)}; 150 | }, ], 151 | ]); 152 | 153 | const primTypeToCountMap = new Map(); 154 | function updateVertAndInstCount(primType, vertexCount, instanceCount) { 155 | const counts = primTypeToCountMap.get(primType) || {vertCount: 0, instCount: 0}; 156 | counts.vertCount += vertexCount * instanceCount; 157 | counts.instCount += instanceCount; 158 | primTypeToCountMap.set(primType, counts); 159 | } 160 | 161 | const handlers = { 162 | bufferData(gl, funcName, info, args) { 163 | const [target, src, /* usage */, srcOffset = 0, length = 0] = args; 164 | if (isNumber(src)) { 165 | return; 166 | } 167 | const isDataView = src instanceof DataView; 168 | const copyLength = length ? length : isDataView 169 | ? src.byteLength - srcOffset 170 | : src.length - srcOffset; 171 | const elemSize = isDataView ? 1 : src.BYTES_PER_ELEMENT; 172 | const bufSize = copyLength * elemSize; 173 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + bufSize); 174 | }, 175 | // WebGL1 176 | // void bufferSubData(GLenum target, GLintptr dstByteOffset, [AllowShared] BufferSource srcData); 177 | // WebGL2 178 | // void bufferSubData(GLenum target, GLintptr dstByteOffset, [AllowShared] ArrayBufferView srcData, 179 | // GLuint srcOffset, optional GLuint length = 0); 180 | bufferSubData(gl, funcName, info, args) { 181 | const [target, dstByteOffset, src, srcOffset = 0, length = 0] = args; 182 | const isDataView = src instanceof DataView; 183 | const copyLength = length ? length : isDataView 184 | ? src.byteLength - srcOffset 185 | : src.length - srcOffset; 186 | const elemSize = isDataView ? 1 : src.BYTES_PER_ELEMENT; 187 | const copySize = copyLength * elemSize; 188 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + copySize); 189 | }, 190 | // undefined getBufferSubData(GLenum target, GLintptr srcByteOffset, [AllowShared] ArrayBufferView dstBuffer, 191 | // optional GLuint dstOffset = 0, optional GLuint length = 0); 192 | getBufferSubData(gl, funcName, info, args) { 193 | const [target, offset, dstBuffer, dstOffset = 0, length = 0] = args; 194 | const isDataView = dstBuffer instanceof DataView; 195 | const copyLength = length ? length : isDataView 196 | ? dstBuffer.byteLength - srcOffset 197 | : dstBuffer.length - srcOffset; 198 | const elemSize = isDataView ? 1 : dstBuffer.BYTES_PER_ELEMENT; 199 | const copySize = copyLength * elemSize; 200 | readTargetToByteCountMap.set(target, (readTargetToByteCountMap.get(target) || 0) + copySize); 201 | }, 202 | readPixels(gl, funcName, info, args) { 203 | const [x, y, width, height, format, type, src] = args; 204 | if (isNumber(src)) { 205 | return; 206 | } 207 | const size = computeBytesForFormatType(width, height, 1, format, type); 208 | const target = 'readPixels'; 209 | readTargetToByteCountMap.set(target, (readTargetToByteCountMap.get(target) || 0) + size); 210 | }, 211 | texImage2D(gl, funcName, info, args) { 212 | const parser = texImage2DArgParsersMap.get(args.length); 213 | const {target, level, internalFormat, width, height, format, type, src} = parser(args); 214 | if (!src) { 215 | return; 216 | } 217 | const size = computeBytesForFormatType(width, height, 1, format, type); 218 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + size); 219 | }, 220 | texSubImage2D(gl, funcName, info, args) { 221 | const parser = texSubImage2DArgParsersMap.get(args.length); 222 | const {target, width, height, format, type, src} = parser(args); 223 | if (!src) { 224 | return; 225 | } 226 | const size = computeBytesForFormatType(width, height, 1, format, type); 227 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + size); 228 | }, 229 | texImage3D(gl, funcName, info, args) { 230 | const parser = texImage3DArgParsersMap.get(args.length); 231 | const {target, width, height, depth, format, type, src} = parser(args); 232 | if (!src) { 233 | return; 234 | } 235 | const size = computeBytesForFormatType(width, height, depth, format, type); 236 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + size); 237 | }, 238 | texSubImage3D(gl, funcName, info, args) { 239 | const parser = texSubImage3DArgParsersMap.get(args.length); 240 | const {target, width, height, depth, format, type, src} = parser(args); 241 | if (!src) { 242 | return; 243 | } 244 | const size = computeBytesForFormatType(width, height, depth, format, type); 245 | targetToByteCountMap.set(target, (targetToByteCountMap.get(target) || 0) + size); 246 | }, 247 | drawArrays(gl, funcName, info, args) { 248 | const [primType, startOffset, vertCount] = args; 249 | updateVertAndInstCount(primType, vertCount, 1); 250 | }, 251 | drawElements(gl, funcName, info, args) { 252 | const [primType, vertCount, indexType, startOffset] = args; 253 | updateVertAndInstCount(primType, vertCount, 1); 254 | }, 255 | drawArraysInstanced(gl, funcName, info, args) { 256 | const [primType, startOffset, vertCount, instances] = args; 257 | updateVertAndInstCount(primType, vertCount, instances); 258 | }, 259 | drawElementsInstanced(gl, funcName, info, args) { 260 | const [primType, vertCount, indexType, startOffset, instances] = args; 261 | updateVertAndInstCount(primType, vertCount, instances); 262 | }, 263 | drawArraysInstancedANGLE(gl, funcName, info, args) { 264 | const [primType, startOffset, vertCount, instances] = args; 265 | updateVertAndInstCount(primType, vertCount, instacnes); 266 | }, 267 | drawElementsInstancedANGLE(gl, funcName, info, args) { 268 | const [primType, vertCount, indexType, startOffset, instances] = args; 269 | updateVertAndInstCount(primType, vertCount, instances); 270 | }, 271 | drawRangeElements(gl, funcName, info, args) { 272 | const [primType, start, end, vertCount, indexType, startOffset] = args; 273 | updateVertAndInstCount(primType, vertCount, 1); 274 | }, 275 | }; 276 | 277 | const wrappers = { 278 | bufferData: {category: 'buffer' }, 279 | bufferSubData: {category: 'buffer' }, 280 | 281 | getBufferSubData: {category: 'read'}, 282 | readPixels: {category: 'read'}, 283 | 284 | drawArrays: {category: 'draw'}, 285 | drawElements: {category: 'draw'}, 286 | drawArraysInstanced: {category: 'draw'}, 287 | drawElementsInstanced: {category: 'draw'}, 288 | drawRangeElements: {category: 'draw'}, 289 | 290 | getActiveUniform: {category: 'get'}, 291 | getActiveUniformBlockName: {category: 'get'}, 292 | getActiveUniformBlockParameter: {category: 'get'}, 293 | getActiveUniforms: {category: 'get'}, 294 | getAttachedShaders: {category: 'get'}, 295 | getBufferParameter: {category: 'get'}, 296 | getContextAttributes: {category: 'get'}, 297 | getError: {category: 'getError'}, 298 | getExtension: {category: 'get'}, 299 | getFragDataLocation: {category: 'get'}, 300 | getFramebufferAttachmentParameter: {category: 'get'}, 301 | getIndexedParameter: {category: 'get'}, 302 | getInternalformatParameter: {category: 'get'}, 303 | getParameter: {category: 'get'}, 304 | getProgramInfoLog: {category: 'get'}, 305 | getProgramParameter: {category: 'get'}, 306 | getQuery: {category: 'get'}, 307 | getQueryParameter: {category: 'get'}, 308 | getRenderbufferParameter: {category: 'get'}, 309 | getSamplerParameter: {category: 'get'}, 310 | getShaderInfoLog: {category: 'get'}, 311 | getShaderParameter: {category: 'get'}, 312 | getShaderPrecisionFormat: {category: 'get'}, 313 | getShaderSource: {category: 'get'}, 314 | getSupportedExtensions: {category: 'get'}, 315 | getSyncParameter: {category: 'get'}, 316 | getTexParameter: {category: 'get'}, 317 | getTransformFeedbackVarying: {category: 'get'}, 318 | getUniform: {category: 'get'}, 319 | getUniformBlockIndex: {category: 'get'}, 320 | getUniformIndices: {category: 'get'}, 321 | getVertexAttrib: {category: 'get'}, 322 | getVertexAttribOffset: {category: 'get'}, 323 | 324 | getAttribLocation: {category: 'getUniform/Attrib'}, 325 | getUniformLocation: {category: 'getUniform/Attrib'}, 326 | 327 | texImage2D: {category: 'texture'}, 328 | texSubImage2D: {category: 'texture'}, 329 | texImage3D: {category: 'texture'}, 330 | texSubImage3D: {category: 'texture'}, 331 | compressedTexImage2D: {category: 'texture'}, 332 | compressedTexSubImage2D: {category: 'texture'}, 333 | compressedTexImage3D: {category: 'texture'}, 334 | compressedTexSubImage3D: {category: 'texture'}, 335 | 336 | enableVertexAttribArray: {category: 'attribs'}, 337 | vertexAttribPointer: {category: 'attribs'}, 338 | vertexAttribIPointer: {category: 'attribs'}, 339 | vertexAttribDivisor: {category: 'attribs'}, 340 | 341 | uniform1ui: {category: 'uniforms'}, 342 | uniform2ui: {category: 'uniforms'}, 343 | uniform3ui: {category: 'uniforms'}, 344 | uniform4ui: {category: 'uniforms'}, 345 | uniformBlockBinding: {category: 'uniforms'}, 346 | uniform1f: {category: 'uniforms'}, 347 | uniform1fv: {category: 'uniforms'}, 348 | uniform1i: {category: 'uniforms'}, 349 | uniform1iv: {category: 'uniforms'}, 350 | uniform1uiv: {category: 'uniforms'}, 351 | uniform2f: {category: 'uniforms'}, 352 | uniform2fv: {category: 'uniforms'}, 353 | uniform2i: {category: 'uniforms'}, 354 | uniform2iv: {category: 'uniforms'}, 355 | uniform2uiv: {category: 'uniforms'}, 356 | uniform3f: {category: 'uniforms'}, 357 | uniform3fv: {category: 'uniforms'}, 358 | uniform3i: {category: 'uniforms'}, 359 | uniform3iv: {category: 'uniforms'}, 360 | uniform3uiv: {category: 'uniforms'}, 361 | uniform4f: {category: 'uniforms'}, 362 | uniform4fv: {category: 'uniforms'}, 363 | uniform4i: {category: 'uniforms'}, 364 | uniform4iv: {category: 'uniforms'}, 365 | uniform4uiv: {category: 'uniforms'}, 366 | uniformMatrix2fv: {category: 'uniforms'}, 367 | uniformMatrix2x3fv: {category: 'uniforms'}, 368 | uniformMatrix2x4fv: {category: 'uniforms'}, 369 | uniformMatrix3fv: {category: 'uniforms'}, 370 | uniformMatrix3x2fv: {category: 'uniforms'}, 371 | uniformMatrix3x4fv: {category: 'uniforms'}, 372 | uniformMatrix4fv: {category: 'uniforms'}, 373 | uniformMatrix4x2fv: {category: 'uniforms'}, 374 | uniformMatrix4x3fv: {category: 'uniforms'}, 375 | }; 376 | 377 | const badCategories = { 378 | 'attribs': true, 379 | 'get': true, 380 | 'getError': true, 381 | 'getUniform/Attrib': true, 382 | 'read': true, 383 | }; 384 | 385 | const counts = new Map(); 386 | 387 | function wrap(api, fnName, origFn, info) { 388 | const handler = handlers[fnName]; 389 | return function(...args) { 390 | if (info.track !== false) { 391 | counts.set(fnName, (counts.get(fnName) || 0) + 1); 392 | } 393 | if (handler) { 394 | handler(this, fnName, info, args) 395 | } 396 | return origFn.call(this, ...args); 397 | } 398 | } 399 | 400 | function wrapAPI(api) { 401 | for (const [fnName, info] of Object.entries(wrappers)) { 402 | const origFn = api[fnName]; 403 | if (origFn) { 404 | api[fnName] = wrap(api, fnName, origFn, info); 405 | } 406 | } 407 | } 408 | wrapAPI(WebGL2RenderingContext.prototype); 409 | wrapAPI(WebGLRenderingContext.prototype); 410 | 411 | let showDetails = false; 412 | const elem = document.createElement('div'); 413 | elem.className = 'webgl-show-info'; 414 | elem.style.position = 'fixed'; 415 | elem.style.left = '0'; 416 | elem.style.top = '0'; 417 | elem.style.backgroundColor = '#000'; 418 | elem.style.color = '#FFF'; 419 | elem.style.padding = '0.5em'; 420 | elem.style.lineHeight = '1'; 421 | elem.style.zIndex = '10000'; 422 | const addToBody = () => { 423 | document.body.appendChild(elem); 424 | }; 425 | if (!document.body) { 426 | window.addEventListener('load', addToBody); 427 | } else { 428 | addToBody(); 429 | } 430 | elem.addEventListener('click', () => {showDetails = !showDetails}); 431 | 432 | // this is a bad hack but I don't want to refactor. 433 | // Store the counts so that below 1 they are a timer until we stop showing 434 | function trickleDown(v) { 435 | return v >= 1 ? 0.99 : Math.max(0, v - 0.0025); 436 | } 437 | 438 | function getPrimCounts(lines) { 439 | for (const [primType, counts] of primTypeToCountMap.entries()) { 440 | if (counts.vertCount > 0 || counts.instCount > 0) { 441 | lines.push(`${enumToString(primType)}: verts: ${counts.vertCount | 0}, instances: ${counts.instCount | 0}`, false, Math.max(counts.vertCount, counts.instCount)); 442 | counts.vertCount = trickleDown(counts.vertCount); 443 | counts.instCount = trickleDown(counts.instCount); 444 | } 445 | } 446 | } 447 | 448 | function getReadByteTransferDetails(lines) { 449 | for (const [target, bytes] of readTargetToByteCountMap.entries()) { 450 | if (bytes > 0) { 451 | lines.push(`${enumToString(target)}: ${bytes | 0}`, true, bytes); 452 | readTargetToByteCountMap.set(target, trickleDown(bytes)); 453 | } 454 | } 455 | } 456 | 457 | function getByteTransferDetails(lines) { 458 | for (const [target, bytes] of targetToByteCountMap.entries()) { 459 | if (bytes > 0) { 460 | const bad = target === ELEMENT_ARRAY_BUFFER && bytes > 0; 461 | lines.push(`${enumToString(target)}: ${bytes | 0}`, bad, bytes); 462 | targetToByteCountMap.set(target, trickleDown(bytes)); 463 | } 464 | } 465 | } 466 | 467 | function getDetails(lines) { 468 | for (const [fnName, count] of counts.entries()) { 469 | if (count > 0) { 470 | const {category} = wrappers[fnName]; 471 | lines.push(`${fnName}: ${count | 0}`, badCategories[category] && count, count); 472 | counts.set(fnName, trickleDown(count)); 473 | } 474 | } 475 | } 476 | 477 | function getOverview(lines) { 478 | const categories = new Map(); 479 | for (const [fnName, count] of counts.entries()) { 480 | const {category} = wrappers[fnName]; 481 | categories.set(category, (categories.get(category) || 0) + count | 0) 482 | counts.set(fnName, trickleDown(count)); 483 | } 484 | for (const [category, count] of categories.entries()) { 485 | if (count > 0) { 486 | lines.push(`${category} calls: ${count | 0}`, badCategories[category] && count, count); 487 | } 488 | } 489 | } 490 | 491 | class LineManager { 492 | constructor(rootElem) { 493 | this.rootElem = rootElem; 494 | this.lineElements = []; 495 | this.index = 0; 496 | } 497 | _getElem() { 498 | if (this.index == this.lineElements.length) { 499 | const elem = document.createElement('pre'); 500 | elem.style.margin = '0'; 501 | this.rootElem.appendChild(elem); 502 | this.lineElements.push(elem); 503 | } 504 | const elem =this.lineElements[this.index++]; 505 | elem.style.display = ''; 506 | return elem; 507 | } 508 | push(msg, bad = false, opacity = 1) { 509 | const elem = this._getElem(); 510 | elem.textContent = msg; 511 | elem.style.color = `rgba(${bad ? '255, 0, 0' : '255, 255, 255'}, ${Math.min(opacity, 1)})`; 512 | } 513 | finish() { 514 | for (let i = this.index; i < this.lineElements.length; ++i) { 515 | this.lineElements[i].style.display = 'none'; 516 | } 517 | this.index = 0; 518 | } 519 | } 520 | 521 | const lines = new LineManager(elem); 522 | 523 | let frameCount = 0; 524 | let then = 0; 525 | function update(now) { 526 | const deltaTime = now - then; 527 | then = now; 528 | lines.push(`frame count: ${frameCount++}, fps: ${(1000 / deltaTime).toFixed(2)}`); 529 | lines.push('\n--- [ Primitives ] ---'); 530 | getPrimCounts(lines); 531 | lines.push('\n---[ Data Transfer (in bytes) ] ---'); 532 | getByteTransferDetails(lines); 533 | if (readTargetToByteCountMap.size) { 534 | lines.push('\n---[ Read Transfer (in bytes) ] ---'); 535 | getReadByteTransferDetails(lines); 536 | } 537 | lines.push('\n---[ Call Counts ] ---'); 538 | showDetails ? getDetails(lines) : getOverview(lines); 539 | lines.finish(); 540 | requestAnimationFrame(update); 541 | } 542 | requestAnimationFrame(update) 543 | }()); -------------------------------------------------------------------------------- /webgl2-disable.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | if (typeof HTMLCanvasElement !== "undefined") { 4 | wrapGetContext(HTMLCanvasElement); 5 | } 6 | if (typeof OffscreenCanvas !== "undefined") { 7 | wrapGetContext(OffscreenCanvas); 8 | } 9 | 10 | function wrapGetContext(ContextClass) { 11 | ContextClass.prototype.getContext = function(origFn) { 12 | return function(type, attributes) { 13 | if (type === 'webgl2') { 14 | return null; 15 | } 16 | return origFn.apply(this, arguments); 17 | }; 18 | }(ContextClass.prototype.getContext); 19 | } 20 | 21 | }()); 22 | --------------------------------------------------------------------------------