webgl-debug-helper.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. /*
  2. * Copyright 2012, Gregg Tavares.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following disclaimer
  13. * in the documentation and/or other materials provided with the
  14. * distribution.
  15. * * Neither the name of Gregg Tavares. nor the names of his
  16. * contributors may be used to endorse or promote products derived from
  17. * this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. /* global define, globalThis */
  32. (function(root, factory) { // eslint-disable-line
  33. if (typeof define === 'function' && define.amd) {
  34. // AMD. Register as an anonymous module.
  35. define([], function() {
  36. return factory.call(root);
  37. });
  38. } else {
  39. // Browser globals
  40. root.webglDebugHelper = factory.call(root);
  41. }
  42. }(this || globalThis, function() {
  43. 'use strict'; // eslint-disable-line
  44. //------------ [ from https://github.com/KhronosGroup/WebGLDeveloperTools ]
  45. /*
  46. ** Copyright (c) 2012 The Khronos Group Inc.
  47. **
  48. ** Permission is hereby granted, free of charge, to any person obtaining a
  49. ** copy of this software and/or associated documentation files (the
  50. ** "Materials"), to deal in the Materials without restriction, including
  51. ** without limitation the rights to use, copy, modify, merge, publish,
  52. ** distribute, sublicense, and/or sell copies of the Materials, and to
  53. ** permit persons to whom the Materials are furnished to do so, subject to
  54. ** the following conditions:
  55. **
  56. ** The above copyright notice and this permission notice shall be included
  57. ** in all copies or substantial portions of the Materials.
  58. **
  59. ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  60. ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  61. ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  62. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  63. ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  64. ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  65. ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
  66. */
  67. /**
  68. * Types of contexts we have added to map
  69. */
  70. const mappedContextTypes = {};
  71. /**
  72. * Map of numbers to names.
  73. * @type {Object}
  74. */
  75. const glEnums = {};
  76. /**
  77. * Map of names to numbers.
  78. * @type {Object}
  79. */
  80. const enumStringToValue = {};
  81. /**
  82. * Initializes this module. Safe to call more than once.
  83. * @param {!WebGLRenderingContext} ctx A WebGL context. If
  84. * you have more than one context it doesn't matter which one
  85. * you pass in, it is only used to pull out constants.
  86. */
  87. function addEnumsForContext(ctx, type) {
  88. if (!mappedContextTypes[type]) {
  89. mappedContextTypes[type] = true;
  90. for (const propertyName in ctx) {
  91. if (typeof ctx[propertyName] === 'number') {
  92. glEnums[ctx[propertyName]] = propertyName;
  93. enumStringToValue[propertyName] = ctx[propertyName];
  94. }
  95. }
  96. }
  97. }
  98. function enumArrayToString(enums) {
  99. const enumStrings = [];
  100. if (enums.length) {
  101. for (let i = 0; i < enums.length; ++i) {
  102. enums.push(glEnumToString(enums[i])); // eslint-disable-line
  103. }
  104. return '[' + enumStrings.join(', ') + ']';
  105. }
  106. return enumStrings.toString();
  107. }
  108. function makeBitFieldToStringFunc(enums) {
  109. return function(value) {
  110. let orResult = 0;
  111. const orEnums = [];
  112. for (let i = 0; i < enums.length; ++i) {
  113. const enumValue = enumStringToValue[enums[i]];
  114. if ((value & enumValue) !== 0) {
  115. orResult |= enumValue;
  116. orEnums.push(glEnumToString(enumValue)); // eslint-disable-line
  117. }
  118. }
  119. if (orResult === value) {
  120. return orEnums.join(' | ');
  121. } else {
  122. return glEnumToString(value); // eslint-disable-line
  123. }
  124. };
  125. }
  126. const destBufferBitFieldToString = makeBitFieldToStringFunc([
  127. 'COLOR_BUFFER_BIT',
  128. 'DEPTH_BUFFER_BIT',
  129. 'STENCIL_BUFFER_BIT',
  130. ]);
  131. /**
  132. * Which arguments are enums based on the number of arguments to the function.
  133. * So
  134. * 'texImage2D': {
  135. * 9: { 0:true, 2:true, 6:true, 7:true },
  136. * 6: { 0:true, 2:true, 3:true, 4:true },
  137. * },
  138. *
  139. * means if there are 9 arguments then 6 and 7 are enums, if there are 6
  140. * arguments 3 and 4 are enums. Maybe a function as well in which case
  141. * value is passed to function and returns a string
  142. *
  143. * @type {!Object.<number, (!Object.<number, string>|function)}
  144. */
  145. const glValidEnumContexts = {
  146. // Generic setters and getters
  147. 'enable': {1: { 0:true }},
  148. 'disable': {1: { 0:true }},
  149. 'getParameter': {1: { 0:true }},
  150. // Rendering
  151. 'drawArrays': {3:{ 0:true }},
  152. 'drawElements': {4:{ 0:true, 2:true }},
  153. 'drawArraysInstanced': {4: { 0:true }},
  154. 'drawElementsInstanced': {5: {0:true, 2: true }},
  155. 'drawRangeElements': {6: {0:true, 4: true }},
  156. // Shaders
  157. 'createShader': {1: { 0:true }},
  158. 'getShaderParameter': {2: { 1:true }},
  159. 'getProgramParameter': {2: { 1:true }},
  160. 'getShaderPrecisionFormat': {2: { 0: true, 1:true }},
  161. // Vertex attributes
  162. 'getVertexAttrib': {2: { 1:true }},
  163. 'vertexAttribPointer': {6: { 2:true }},
  164. 'vertexAttribIPointer': {5: { 2:true }}, // WebGL2
  165. // Textures
  166. 'bindTexture': {2: { 0:true }},
  167. 'activeTexture': {1: { 0:true }},
  168. 'getTexParameter': {2: { 0:true, 1:true }},
  169. 'texParameterf': {3: { 0:true, 1:true }},
  170. 'texParameteri': {3: { 0:true, 1:true, 2:true }},
  171. 'texImage2D': {
  172. 9: { 0:true, 2:true, 6:true, 7:true },
  173. 6: { 0:true, 2:true, 3:true, 4:true },
  174. 10: { 0:true, 2:true, 6:true, 7:true }, // WebGL2
  175. },
  176. 'texImage3D': {
  177. 10: { 0:true, 2:true, 7:true, 8:true }, // WebGL2
  178. 11: { 0:true, 2:true, 7:true, 8:true }, // WebGL2
  179. },
  180. 'texSubImage2D': {
  181. 9: { 0:true, 6:true, 7:true },
  182. 7: { 0:true, 4:true, 5:true },
  183. 10: { 0:true, 6:true, 7:true }, // WebGL2
  184. },
  185. 'texSubImage3D': {
  186. 11: { 0:true, 8:true, 9:true }, // WebGL2
  187. 12: { 0:true, 8:true, 9:true }, // WebGL2
  188. },
  189. 'texStorage2D': { 5: { 0:true, 2:true }}, // WebGL2
  190. 'texStorage3D': { 6: { 0:true, 2:true }}, // WebGL2
  191. 'copyTexImage2D': {8: { 0:true, 2:true }},
  192. 'copyTexSubImage2D': {8: { 0:true }},
  193. 'copyTexSubImage3D': {9: { 0:true }}, // WebGL2
  194. 'generateMipmap': {1: { 0:true }},
  195. 'compressedTexImage2D': {
  196. 7: { 0: true, 2:true },
  197. 8: { 0: true, 2:true }, // WebGL2
  198. },
  199. 'compressedTexSubImage2D': {
  200. 8: { 0: true, 6:true },
  201. 9: { 0: true, 6:true }, // WebGL2
  202. },
  203. 'compressedTexImage3D': {
  204. 8: { 0: true, 2: true, }, // WebGL2
  205. 9: { 0: true, 2: true, }, // WebGL2
  206. },
  207. 'compressedTexSubImage3D': {
  208. 9: { 0: true, 8: true, }, // WebGL2
  209. 10: { 0: true, 8: true, }, // WebGL2
  210. },
  211. // Buffer objects
  212. 'bindBuffer': {2: { 0:true }},
  213. 'bufferData': {
  214. 3: { 0:true, 2:true },
  215. 4: { 0:true, 2:true }, // WebGL2
  216. 5: { 0:true, 2:true }, // WebGL2
  217. },
  218. 'bufferSubData': {
  219. 3: { 0:true },
  220. 4: { 0:true }, // WebGL2
  221. 5: { 0:true }, // WebGL2
  222. },
  223. 'copyBufferSubData': {
  224. 5: { 0:true }, // WeBGL2
  225. },
  226. 'getBufferParameter': {2: { 0:true, 1:true }},
  227. 'getBufferSubData': {
  228. 3: { 0: true, }, // WebGL2
  229. 4: { 0: true, }, // WebGL2
  230. 5: { 0: true, }, // WebGL2
  231. },
  232. // Renderbuffers and framebuffers
  233. 'pixelStorei': {2: { 0:true, 1:true }},
  234. 'readPixels': {
  235. 7: { 4:true, 5:true },
  236. 8: { 4:true, 5:true }, // WebGL2
  237. },
  238. 'bindRenderbuffer': {2: { 0:true }},
  239. 'bindFramebuffer': {2: { 0:true }},
  240. 'blitFramebuffer': {10: { 8: destBufferBitFieldToString, 9:true }}, // WebGL2
  241. 'checkFramebufferStatus': {1: { 0:true }},
  242. 'framebufferRenderbuffer': {4: { 0:true, 1:true, 2:true }},
  243. 'framebufferTexture2D': {5: { 0:true, 1:true, 2:true }},
  244. 'framebufferTextureLayer': {5: {0:true, 1:true }}, // WebGL2
  245. 'getFramebufferAttachmentParameter': {3: { 0:true, 1:true, 2:true }},
  246. 'getInternalformatParameter': {3: {0:true, 1:true, 2:true }}, // WebGL2
  247. 'getRenderbufferParameter': {2: { 0:true, 1:true }},
  248. 'invalidateFramebuffer': {2: { 0:true, 1: enumArrayToString, }}, // WebGL2
  249. 'invalidateSubFramebuffer': {6: {0: true, 1: enumArrayToString, }}, // WebGL2
  250. 'readBuffer': {1: {0: true}}, // WebGL2
  251. 'renderbufferStorage': {4: { 0:true, 1:true }},
  252. 'renderbufferStorageMultisample': {5: { 0: true, 2: true }}, // WebGL2
  253. // Frame buffer operations (clear, blend, depth test, stencil)
  254. 'clear': {1: { 0: destBufferBitFieldToString }},
  255. 'depthFunc': {1: { 0:true }},
  256. 'blendFunc': {2: { 0:true, 1:true }},
  257. 'blendFuncSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
  258. 'blendEquation': {1: { 0:true }},
  259. 'blendEquationSeparate': {2: { 0:true, 1:true }},
  260. 'stencilFunc': {3: { 0:true }},
  261. 'stencilFuncSeparate': {4: { 0:true, 1:true }},
  262. 'stencilMaskSeparate': {2: { 0:true }},
  263. 'stencilOp': {3: { 0:true, 1:true, 2:true }},
  264. 'stencilOpSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
  265. // Culling
  266. 'cullFace': {1: { 0:true }},
  267. 'frontFace': {1: { 0:true }},
  268. // ANGLE_instanced_arrays extension
  269. 'drawArraysInstancedANGLE': {4: { 0:true }},
  270. 'drawElementsInstancedANGLE': {5: { 0:true, 2:true }},
  271. // EXT_blend_minmax extension
  272. 'blendEquationEXT': {1: { 0:true }},
  273. // Multiple Render Targets
  274. 'drawBuffersWebGL': {1: {0: enumArrayToString, }}, // WEBGL_draw_bufers
  275. 'drawBuffers': {1: {0: enumArrayToString, }}, // WebGL2
  276. 'clearBufferfv': {
  277. 4: {0: true }, // WebGL2
  278. 5: {0: true }, // WebGL2
  279. },
  280. 'clearBufferiv': {
  281. 4: {0: true }, // WebGL2
  282. 5: {0: true }, // WebGL2
  283. },
  284. 'clearBufferuiv': {
  285. 4: {0: true }, // WebGL2
  286. 5: {0: true }, // WebGL2
  287. },
  288. 'clearBufferfi': { 4: {0: true}}, // WebGL2
  289. // QueryObjects
  290. 'beginQuery': { 2: { 0: true }}, // WebGL2
  291. 'endQuery': { 1: { 0: true }}, // WebGL2
  292. 'getQuery': { 2: { 0: true, 1: true }}, // WebGL2
  293. 'getQueryParameter': { 2: { 1: true }}, // WebGL2
  294. // Sampler Objects
  295. 'samplerParameteri': { 3: { 1: true }}, // WebGL2
  296. 'samplerParameterf': { 3: { 1: true }}, // WebGL2
  297. 'getSamplerParameter': { 2: { 1: true }}, // WebGL2
  298. // Sync objects
  299. 'clientWaitSync': { 3: { 1: makeBitFieldToStringFunc(['SYNC_FLUSH_COMMANDS_BIT']) }}, // WebGL2
  300. 'fenceSync': { 2: { 0: true }}, // WebGL2
  301. 'getSyncParameter': { 2: { 1: true }}, // WebGL2
  302. // Transform Feedback
  303. 'bindTransformFeedback': { 2: { 0: true }}, // WebGL2
  304. 'beginTransformFeedback': { 1: { 0: true }}, // WebGL2
  305. // Uniform Buffer Objects and Transform Feedback Buffers
  306. 'bindBufferBase': { 3: { 0: true }}, // WebGL2
  307. 'bindBufferRange': { 5: { 0: true }}, // WebGL2
  308. 'getIndexedParameter': { 2: { 0: true }}, // WebGL2
  309. 'getActiveUniforms': { 3: { 2: true }}, // WebGL2
  310. 'getActiveUniformBlockParameter': { 3: { 2: true }}, // WebGL2
  311. };
  312. /**
  313. * Gets an string version of an WebGL enum.
  314. *
  315. * Example:
  316. * var str = WebGLDebugUtil.glEnumToString(ctx.getError());
  317. *
  318. * @param {number} value Value to return an enum for
  319. * @return {string} The string version of the enum.
  320. */
  321. function glEnumToString(value) {
  322. const name = glEnums[value];
  323. return (name !== undefined)
  324. ? `gl.${name}`
  325. : `/*UNKNOWN WebGL ENUM*/ 0x${value.toString(16)}`;
  326. }
  327. /**
  328. * Returns the string version of a WebGL argument.
  329. * Attempts to convert enum arguments to strings.
  330. * @param {string} functionName the name of the WebGL function.
  331. * @param {number} numArgs the number of arguments passed to the function.
  332. * @param {number} argumentIndx the index of the argument.
  333. * @param {*} value The value of the argument.
  334. * @return {string} The value as a string.
  335. */
  336. function glFunctionArgToString(functionName, numArgs, argumentIndex, value) {
  337. const funcInfos = glValidEnumContexts[functionName];
  338. if (funcInfos !== undefined) {
  339. const funcInfo = funcInfos[numArgs];
  340. if (funcInfo !== undefined) {
  341. const argType = funcInfo[argumentIndex];
  342. if (argType) {
  343. if (typeof argType === 'function') {
  344. return argType(value);
  345. } else {
  346. return glEnumToString(value);
  347. }
  348. }
  349. }
  350. }
  351. if (value === null) {
  352. return 'null';
  353. } else if (value === undefined) {
  354. return 'undefined';
  355. } else {
  356. return value.toString();
  357. }
  358. }
  359. /**
  360. * Converts the arguments of a WebGL function to a string.
  361. * Attempts to convert enum arguments to strings.
  362. *
  363. * @param {string} functionName the name of the WebGL function.
  364. * @param {number} args The arguments.
  365. * @return {string} The arguments as a string.
  366. */
  367. function glFunctionArgsToString(functionName, args) {
  368. // apparently we can't do args.join(",");
  369. const argStrs = [];
  370. const numArgs = args.length;
  371. for (let ii = 0; ii < numArgs; ++ii) {
  372. argStrs.push(glFunctionArgToString(functionName, numArgs, ii, args[ii]));
  373. }
  374. return argStrs.join(', ');
  375. }
  376. function makePropertyWrapper(wrapper, original, propertyName) {
  377. wrapper.__defineGetter__(propertyName, function() { // eslint-disable-line
  378. return original[propertyName];
  379. });
  380. // TODO(gmane): this needs to handle properties that take more than
  381. // one value?
  382. wrapper.__defineSetter__(propertyName, function(value) { // eslint-disable-line
  383. original[propertyName] = value;
  384. });
  385. }
  386. /**
  387. * Given a WebGL context returns a wrapped context that calls
  388. * gl.getError after every command and calls a function if the
  389. * result is not gl.NO_ERROR.
  390. *
  391. * @param {!WebGLRenderingContext} ctx The webgl context to
  392. * wrap.
  393. * @param {!function(err, funcName, args): void} opt_onErrorFunc
  394. * The function to call when gl.getError returns an
  395. * error. If not specified the default function calls
  396. * console.log with a message.
  397. * @param {!function(funcName, args): void} opt_onFunc The
  398. * function to call when each webgl function is called.
  399. * You can use this to log all calls for example.
  400. * @param {!WebGLRenderingContext} opt_err_ctx The webgl context
  401. * to call getError on if different than ctx.
  402. */
  403. function makeDebugContext(ctx, options) {
  404. options = options || {};
  405. const errCtx = options.errCtx || ctx;
  406. const onFunc = options.funcFunc;
  407. const sharedState = options.sharedState || {
  408. numDrawCallsRemaining: options.maxDrawCalls || -1,
  409. wrappers: {},
  410. };
  411. options.sharedState = sharedState;
  412. const errorFunc = options.errorFunc || function(err, functionName, args) {
  413. console.error(`WebGL error ${glEnumToString(err)} in ${functionName}(${glFunctionArgsToString(functionName, args)})`); /* eslint-disable-line no-console */
  414. };
  415. // Holds booleans for each GL error so after we get the error ourselves
  416. // we can still return it to the client app.
  417. const glErrorShadow = { };
  418. const wrapper = {};
  419. function removeChecks() {
  420. Object.keys(sharedState.wrappers).forEach(function(name) {
  421. const pair = sharedState.wrappers[name];
  422. const wrapper = pair.wrapper;
  423. const orig = pair.orig;
  424. for (const propertyName in wrapper) {
  425. if (typeof wrapper[propertyName] === 'function') {
  426. wrapper[propertyName] = orig[propertyName].bind(orig);
  427. }
  428. }
  429. });
  430. }
  431. function checkMaxDrawCalls() {
  432. if (sharedState.numDrawCallsRemaining === 0) {
  433. removeChecks();
  434. }
  435. --sharedState.numDrawCallsRemaining;
  436. }
  437. function noop() {
  438. }
  439. // Makes a function that calls a WebGL function and then calls getError.
  440. function makeErrorWrapper(ctx, functionName) {
  441. const check = functionName.substring(0, 4) === 'draw' ? checkMaxDrawCalls : noop;
  442. return function() {
  443. if (onFunc) {
  444. onFunc(functionName, arguments);
  445. }
  446. const result = ctx[functionName].apply(ctx, arguments);
  447. const err = errCtx.getError();
  448. if (err !== 0) {
  449. glErrorShadow[err] = true;
  450. errorFunc(err, functionName, arguments);
  451. }
  452. check();
  453. return result;
  454. };
  455. }
  456. function makeGetExtensionWrapper(ctx, wrapped) {
  457. return function() {
  458. const extensionName = arguments[0];
  459. let ext = sharedState.wrappers[extensionName];
  460. if (!ext) {
  461. ext = wrapped.apply(ctx, arguments);
  462. if (ext) {
  463. const origExt = ext;
  464. ext = makeDebugContext(ext, {...options, errCtx: ctx});
  465. sharedState.wrappers[extensionName] = { wrapper: ext, orig: origExt };
  466. addEnumsForContext(origExt, extensionName);
  467. }
  468. }
  469. return ext;
  470. };
  471. }
  472. // Make a an object that has a copy of every property of the WebGL context
  473. // but wraps all functions.
  474. for (const propertyName in ctx) {
  475. if (typeof ctx[propertyName] === 'function') {
  476. if (propertyName !== 'getExtension') {
  477. wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
  478. } else {
  479. const wrapped = makeErrorWrapper(ctx, propertyName);
  480. wrapper[propertyName] = makeGetExtensionWrapper(ctx, wrapped);
  481. }
  482. } else {
  483. makePropertyWrapper(wrapper, ctx, propertyName);
  484. }
  485. }
  486. // Override the getError function with one that returns our saved results.
  487. if (wrapper.getError) {
  488. wrapper.getError = function() {
  489. for (const err of Object.keys(glErrorShadow)) {
  490. if (glErrorShadow[err]) {
  491. glErrorShadow[err] = false;
  492. return err;
  493. }
  494. }
  495. return ctx.NO_ERROR;
  496. };
  497. }
  498. if (wrapper.bindBuffer) {
  499. sharedState.wrappers['webgl'] = { wrapper: wrapper, orig: ctx };
  500. addEnumsForContext(ctx, ctx.bindBufferBase ? 'WebGL2' : 'WebGL');
  501. }
  502. return wrapper;
  503. }
  504. return {
  505. makeDebugContext,
  506. glFunctionArgsToString,
  507. glFunctionArgToString,
  508. glEnumToString,
  509. };
  510. }));