├── .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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------