Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / webgl / src / sdk / debug / webgl-debug.js
1 /*
2 ** Copyright (c) 2012 The Khronos Group Inc.
3 **
4 ** Permission is hereby granted, free of charge, to any person obtaining a
5 ** copy of this software and/or associated documentation files (the
6 ** "Materials"), to deal in the Materials without restriction, including
7 ** without limitation the rights to use, copy, modify, merge, publish,
8 ** distribute, sublicense, and/or sell copies of the Materials, and to
9 ** permit persons to whom the Materials are furnished to do so, subject to
10 ** the following conditions:
11 **
12 ** The above copyright notice and this permission notice shall be included
13 ** in all copies or substantial portions of the Materials.
14 **
15 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
22 */
23
24 // Various functions for helping debug WebGL apps.
25
26 WebGLDebugUtils = function() {
27
28 /**
29  * Wrapped logging function.
30  * @param {string} msg Message to log.
31  */
32 var log = function(msg) {
33   if (window.console && window.console.log) {
34     window.console.log(msg);
35   }
36 };
37
38 /**
39  * Wrapped error logging function.
40  * @param {string} msg Message to log.
41  */
42 var error = function(msg) {
43   if (window.console && window.console.error) {
44     window.console.error(msg);
45   } else {
46     log(msg);
47   }
48 };
49
50
51 /**
52  * Which arguments are enums based on the number of arguments to the function.
53  * So
54  *    'texImage2D': {
55  *       9: { 0:true, 2:true, 6:true, 7:true },
56  *       6: { 0:true, 2:true, 3:true, 4:true },
57  *    },
58  *
59  * means if there are 9 arguments then 6 and 7 are enums, if there are 6
60  * arguments 3 and 4 are enums
61  *
62  * @type {!Object.<number, !Object.<number, string>}
63  */
64 var glValidEnumContexts = {
65   // Generic setters and getters
66
67   'enable': {1: { 0:true }},
68   'disable': {1: { 0:true }},
69   'getParameter': {1: { 0:true }},
70
71   // Rendering
72
73   'drawArrays': {3:{ 0:true }},
74   'drawElements': {4:{ 0:true, 2:true }},
75
76   // Shaders
77
78   'createShader': {1: { 0:true }},
79   'getShaderParameter': {2: { 1:true }},
80   'getProgramParameter': {2: { 1:true }},
81   'getShaderPrecisionFormat': {2: { 0: true, 1:true }},
82
83   // Vertex attributes
84
85   'getVertexAttrib': {2: { 1:true }},
86   'vertexAttribPointer': {6: { 2:true }},
87
88   // Textures
89
90   'bindTexture': {2: { 0:true }},
91   'activeTexture': {1: { 0:true }},
92   'getTexParameter': {2: { 0:true, 1:true }},
93   'texParameterf': {3: { 0:true, 1:true }},
94   'texParameteri': {3: { 0:true, 1:true, 2:true }},
95   'texImage2D': {
96      9: { 0:true, 2:true, 6:true, 7:true },
97      6: { 0:true, 2:true, 3:true, 4:true },
98   },
99   'texSubImage2D': {
100     9: { 0:true, 6:true, 7:true },
101     7: { 0:true, 4:true, 5:true },
102   },
103   'copyTexImage2D': {8: { 0:true, 2:true }},
104   'copyTexSubImage2D': {8: { 0:true }},
105   'generateMipmap': {1: { 0:true }},
106   'compressedTexImage2D': {7: { 0: true, 2:true }},
107   'compressedTexSubImage2D': {8: { 0: true, 6:true }},
108
109   // Buffer objects
110
111   'bindBuffer': {2: { 0:true }},
112   'bufferData': {3: { 0:true, 2:true }},
113   'bufferSubData': {3: { 0:true }},
114   'getBufferParameter': {2: { 0:true, 1:true }},
115
116   // Renderbuffers and framebuffers
117
118   'pixelStorei': {2: { 0:true, 1:true }},
119   'readPixels': {7: { 4:true, 5:true }},
120   'bindRenderbuffer': {2: { 0:true }},
121   'bindFramebuffer': {2: { 0:true }},
122   'checkFramebufferStatus': {1: { 0:true }},
123   'framebufferRenderbuffer': {4: { 0:true, 1:true, 2:true }},
124   'framebufferTexture2D': {5: { 0:true, 1:true, 2:true }},
125   'getFramebufferAttachmentParameter': {3: { 0:true, 1:true, 2:true }},
126   'getRenderbufferParameter': {2: { 0:true, 1:true }},
127   'renderbufferStorage': {4: { 0:true, 1:true }},
128
129   // Frame buffer operations (clear, blend, depth test, stencil)
130
131   'clear': {1: { 0:true }},
132   'depthFunc': {1: { 0:true }},
133   'blendFunc': {2: { 0:true, 1:true }},
134   'blendFuncSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
135   'blendEquation': {1: { 0:true }},
136   'blendEquationSeparate': {2: { 0:true, 1:true }},
137   'stencilFunc': {3: { 0:true }},
138   'stencilFuncSeparate': {4: { 0:true, 1:true }},
139   'stencilMaskSeparate': {2: { 0:true }},
140   'stencilOp': {3: { 0:true, 1:true, 2:true }},
141   'stencilOpSeparate': {4: { 0:true, 1:true, 2:true, 3:true }},
142
143   // Culling
144
145   'cullFace': {1: { 0:true }},
146   'frontFace': {1: { 0:true }},
147
148   // ANGLE_instanced_arrays extension
149
150   'drawArraysInstancedANGLE': {4: { 0:true }},
151   'drawElementsInstancedANGLE': {5: { 0:true, 2:true }},
152
153   // EXT_blend_minmax extension
154
155   'blendEquationEXT': {1: { 0:true }},
156 };
157
158 /**
159  * Map of numbers to names.
160  * @type {Object}
161  */
162 var glEnums = null;
163
164 /**
165  * Initializes this module. Safe to call more than once.
166  * @param {!WebGLRenderingContext} ctx A WebGL context. If
167  *    you have more than one context it doesn't matter which one
168  *    you pass in, it is only used to pull out constants.
169  */
170 function init(ctx) {
171   if (glEnums == null) {
172     glEnums = { };
173     for (var propertyName in ctx) {
174       if (typeof ctx[propertyName] == 'number') {
175         glEnums[ctx[propertyName]] = propertyName;
176       }
177     }
178   }
179 }
180
181 /**
182  * Checks the utils have been initialized.
183  */
184 function checkInit() {
185   if (glEnums == null) {
186     throw 'WebGLDebugUtils.init(ctx) not called';
187   }
188 }
189
190 /**
191  * Returns true or false if value matches any WebGL enum
192  * @param {*} value Value to check if it might be an enum.
193  * @return {boolean} True if value matches one of the WebGL defined enums
194  */
195 function mightBeEnum(value) {
196   checkInit();
197   return (glEnums[value] !== undefined);
198 }
199
200 /**
201  * Gets an string version of an WebGL enum.
202  *
203  * Example:
204  *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());
205  *
206  * @param {number} value Value to return an enum for
207  * @return {string} The string version of the enum.
208  */
209 function glEnumToString(value) {
210   checkInit();
211   var name = glEnums[value];
212   return (name !== undefined) ? ("gl." + name) :
213       ("/*UNKNOWN WebGL ENUM*/ 0x" + value.toString(16) + "");
214 }
215
216 /**
217  * Returns the string version of a WebGL argument.
218  * Attempts to convert enum arguments to strings.
219  * @param {string} functionName the name of the WebGL function.
220  * @param {number} numArgs the number of arguments passed to the function.
221  * @param {number} argumentIndx the index of the argument.
222  * @param {*} value The value of the argument.
223  * @return {string} The value as a string.
224  */
225 function glFunctionArgToString(functionName, numArgs, argumentIndex, value) {
226   var funcInfo = glValidEnumContexts[functionName];
227   if (funcInfo !== undefined) {
228     var funcInfo = funcInfo[numArgs];
229     if (funcInfo !== undefined) {
230       if (funcInfo[argumentIndex]) {
231         return glEnumToString(value);
232       }
233     }
234   }
235   if (value === null) {
236     return "null";
237   } else if (value === undefined) {
238     return "undefined";
239   } else {
240     return value.toString();
241   }
242 }
243
244 /**
245  * Converts the arguments of a WebGL function to a string.
246  * Attempts to convert enum arguments to strings.
247  *
248  * @param {string} functionName the name of the WebGL function.
249  * @param {number} args The arguments.
250  * @return {string} The arguments as a string.
251  */
252 function glFunctionArgsToString(functionName, args) {
253   // apparently we can't do args.join(",");
254   var argStr = "";
255   var numArgs = args.length;
256   for (var ii = 0; ii < numArgs; ++ii) {
257     argStr += ((ii == 0) ? '' : ', ') +
258         glFunctionArgToString(functionName, numArgs, ii, args[ii]);
259   }
260   return argStr;
261 };
262
263
264 function makePropertyWrapper(wrapper, original, propertyName) {
265   //log("wrap prop: " + propertyName);
266   wrapper.__defineGetter__(propertyName, function() {
267     return original[propertyName];
268   });
269   // TODO(gmane): this needs to handle properties that take more than
270   // one value?
271   wrapper.__defineSetter__(propertyName, function(value) {
272     //log("set: " + propertyName);
273     original[propertyName] = value;
274   });
275 }
276
277 // Makes a function that calls a function on another object.
278 function makeFunctionWrapper(original, functionName) {
279   //log("wrap fn: " + functionName);
280   var f = original[functionName];
281   return function() {
282     //log("call: " + functionName);
283     var result = f.apply(original, arguments);
284     return result;
285   };
286 }
287
288 /**
289  * Given a WebGL context returns a wrapped context that calls
290  * gl.getError after every command and calls a function if the
291  * result is not gl.NO_ERROR.
292  *
293  * @param {!WebGLRenderingContext} ctx The webgl context to
294  *        wrap.
295  * @param {!function(err, funcName, args): void} opt_onErrorFunc
296  *        The function to call when gl.getError returns an
297  *        error. If not specified the default function calls
298  *        console.log with a message.
299  * @param {!function(funcName, args): void} opt_onFunc The
300  *        function to call when each webgl function is called.
301  *        You can use this to log all calls for example.
302  * @param {!WebGLRenderingContext} opt_err_ctx The webgl context
303  *        to call getError on if different than ctx.
304  */
305 function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc, opt_err_ctx) {
306   opt_err_ctx = opt_err_ctx || ctx;
307   init(ctx);
308   opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) {
309         // apparently we can't do args.join(",");
310         var argStr = "";
311         var numArgs = args.length;
312         for (var ii = 0; ii < numArgs; ++ii) {
313           argStr += ((ii == 0) ? '' : ', ') +
314               glFunctionArgToString(functionName, numArgs, ii, args[ii]);
315         }
316         error("WebGL error "+ glEnumToString(err) + " in "+ functionName +
317               "(" + argStr + ")");
318       };
319
320   // Holds booleans for each GL error so after we get the error ourselves
321   // we can still return it to the client app.
322   var glErrorShadow = { };
323
324   // Makes a function that calls a WebGL function and then calls getError.
325   function makeErrorWrapper(ctx, functionName) {
326     return function() {
327       if (opt_onFunc) {
328         opt_onFunc(functionName, arguments);
329       }
330       var result = ctx[functionName].apply(ctx, arguments);
331       var err = opt_err_ctx.getError();
332       if (err != 0) {
333         glErrorShadow[err] = true;
334         opt_onErrorFunc(err, functionName, arguments);
335       }
336       return result;
337     };
338   }
339
340   // Make a an object that has a copy of every property of the WebGL context
341   // but wraps all functions.
342   var wrapper = {};
343   for (var propertyName in ctx) {
344     if (typeof ctx[propertyName] == 'function') {
345        if (propertyName != 'getExtension') {
346           wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
347        } else {
348           var wrapped = makeErrorWrapper(ctx, propertyName);
349           wrapper[propertyName] = function () {
350              var result = wrapped.apply(ctx, arguments);
351              return makeDebugContext(result, opt_onErrorFunc, opt_onFunc, opt_err_ctx);
352           };
353        }
354      } else {
355        makePropertyWrapper(wrapper, ctx, propertyName);
356      }
357   }
358
359   // Override the getError function with one that returns our saved results.
360   wrapper.getError = function() {
361     for (var err in glErrorShadow) {
362       if (glErrorShadow.hasOwnProperty(err)) {
363         if (glErrorShadow[err]) {
364           glErrorShadow[err] = false;
365           return err;
366         }
367       }
368     }
369     return ctx.NO_ERROR;
370   };
371
372   return wrapper;
373 }
374
375 function resetToInitialState(ctx) {
376   var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
377   var tmp = ctx.createBuffer();
378   ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp);
379   for (var ii = 0; ii < numAttribs; ++ii) {
380     ctx.disableVertexAttribArray(ii);
381     ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0);
382     ctx.vertexAttrib1f(ii, 0);
383   }
384   ctx.deleteBuffer(tmp);
385
386   var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
387   for (var ii = 0; ii < numTextureUnits; ++ii) {
388     ctx.activeTexture(ctx.TEXTURE0 + ii);
389     ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null);
390     ctx.bindTexture(ctx.TEXTURE_2D, null);
391   }
392
393   ctx.activeTexture(ctx.TEXTURE0);
394   ctx.useProgram(null);
395   ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
396   ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
397   ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
398   ctx.bindRenderbuffer(ctx.RENDERBUFFER, null);
399   ctx.disable(ctx.BLEND);
400   ctx.disable(ctx.CULL_FACE);
401   ctx.disable(ctx.DEPTH_TEST);
402   ctx.disable(ctx.DITHER);
403   ctx.disable(ctx.SCISSOR_TEST);
404   ctx.blendColor(0, 0, 0, 0);
405   ctx.blendEquation(ctx.FUNC_ADD);
406   ctx.blendFunc(ctx.ONE, ctx.ZERO);
407   ctx.clearColor(0, 0, 0, 0);
408   ctx.clearDepth(1);
409   ctx.clearStencil(-1);
410   ctx.colorMask(true, true, true, true);
411   ctx.cullFace(ctx.BACK);
412   ctx.depthFunc(ctx.LESS);
413   ctx.depthMask(true);
414   ctx.depthRange(0, 1);
415   ctx.frontFace(ctx.CCW);
416   ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE);
417   ctx.lineWidth(1);
418   ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4);
419   ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4);
420   ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false);
421   ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
422   // TODO: Delete this IF.
423   if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) {
424     ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL);
425   }
426   ctx.polygonOffset(0, 0);
427   ctx.sampleCoverage(1, false);
428   ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height);
429   ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF);
430   ctx.stencilMask(0xFFFFFFFF);
431   ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP);
432   ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
433   ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
434
435   // TODO: This should NOT be needed but Firefox fails with 'hint'
436   while(ctx.getError());
437 }
438
439 function makeLostContextSimulatingCanvas(canvas) {
440   var unwrappedContext_;
441   var wrappedContext_;
442   var onLost_ = [];
443   var onRestored_ = [];
444   var wrappedContext_ = {};
445   var contextId_ = 1;
446   var contextLost_ = false;
447   var resourceId_ = 0;
448   var resourceDb_ = [];
449   var numCallsToLoseContext_ = 0;
450   var numCalls_ = 0;
451   var canRestore_ = false;
452   var restoreTimeout_ = 0;
453
454   // Holds booleans for each GL error so can simulate errors.
455   var glErrorShadow_ = { };
456
457   canvas.getContext = function(f) {
458     return function() {
459       var ctx = f.apply(canvas, arguments);
460       // Did we get a context and is it a WebGL context?
461       if (ctx instanceof WebGLRenderingContext) {
462         if (ctx != unwrappedContext_) {
463           if (unwrappedContext_) {
464             throw "got different context"
465           }
466           unwrappedContext_ = ctx;
467           wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_);
468         }
469         return wrappedContext_;
470       }
471       return ctx;
472     }
473   }(canvas.getContext);
474
475   function wrapEvent(listener) {
476     if (typeof(listener) == "function") {
477       return listener;
478     } else {
479       return function(info) {
480         listener.handleEvent(info);
481       }
482     }
483   }
484
485   var addOnContextLostListener = function(listener) {
486     onLost_.push(wrapEvent(listener));
487   };
488
489   var addOnContextRestoredListener = function(listener) {
490     onRestored_.push(wrapEvent(listener));
491   };
492
493
494   function wrapAddEventListener(canvas) {
495     var f = canvas.addEventListener;
496     canvas.addEventListener = function(type, listener, bubble) {
497       switch (type) {
498         case 'webglcontextlost':
499           addOnContextLostListener(listener);
500           break;
501         case 'webglcontextrestored':
502           addOnContextRestoredListener(listener);
503           break;
504         default:
505           f.apply(canvas, arguments);
506       }
507     };
508   }
509
510   wrapAddEventListener(canvas);
511
512   canvas.loseContext = function() {
513     if (!contextLost_) {
514       contextLost_ = true;
515       numCallsToLoseContext_ = 0;
516       ++contextId_;
517       while (unwrappedContext_.getError());
518       clearErrors();
519       glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true;
520       var event = makeWebGLContextEvent("context lost");
521       var callbacks = onLost_.slice();
522       setTimeout(function() {
523           //log("numCallbacks:" + callbacks.length);
524           for (var ii = 0; ii < callbacks.length; ++ii) {
525             //log("calling callback:" + ii);
526             callbacks[ii](event);
527           }
528           if (restoreTimeout_ >= 0) {
529             setTimeout(function() {
530                 canvas.restoreContext();
531               }, restoreTimeout_);
532           }
533         }, 0);
534     }
535   };
536
537   canvas.restoreContext = function() {
538     if (contextLost_) {
539       if (onRestored_.length) {
540         setTimeout(function() {
541             if (!canRestore_) {
542               throw "can not restore. webglcontestlost listener did not call event.preventDefault";
543             }
544             freeResources();
545             resetToInitialState(unwrappedContext_);
546             contextLost_ = false;
547             numCalls_ = 0;
548             canRestore_ = false;
549             var callbacks = onRestored_.slice();
550             var event = makeWebGLContextEvent("context restored");
551             for (var ii = 0; ii < callbacks.length; ++ii) {
552               callbacks[ii](event);
553             }
554           }, 0);
555       }
556     }
557   };
558
559   canvas.loseContextInNCalls = function(numCalls) {
560     if (contextLost_) {
561       throw "You can not ask a lost contet to be lost";
562     }
563     numCallsToLoseContext_ = numCalls_ + numCalls;
564   };
565
566   canvas.getNumCalls = function() {
567     return numCalls_;
568   };
569
570   canvas.setRestoreTimeout = function(timeout) {
571     restoreTimeout_ = timeout;
572   };
573
574   function isWebGLObject(obj) {
575     //return false;
576     return (obj instanceof WebGLBuffer ||
577             obj instanceof WebGLFramebuffer ||
578             obj instanceof WebGLProgram ||
579             obj instanceof WebGLRenderbuffer ||
580             obj instanceof WebGLShader ||
581             obj instanceof WebGLTexture);
582   }
583
584   function checkResources(args) {
585     for (var ii = 0; ii < args.length; ++ii) {
586       var arg = args[ii];
587       if (isWebGLObject(arg)) {
588         return arg.__webglDebugContextLostId__ == contextId_;
589       }
590     }
591     return true;
592   }
593
594   function clearErrors() {
595     var k = Object.keys(glErrorShadow_);
596     for (var ii = 0; ii < k.length; ++ii) {
597       delete glErrorShadow_[k];
598     }
599   }
600
601   function loseContextIfTime() {
602     ++numCalls_;
603     if (!contextLost_) {
604       if (numCallsToLoseContext_ == numCalls_) {
605         canvas.loseContext();
606       }
607     }
608   }
609
610   // Makes a function that simulates WebGL when out of context.
611   function makeLostContextFunctionWrapper(ctx, functionName) {
612     var f = ctx[functionName];
613     return function() {
614       // log("calling:" + functionName);
615       // Only call the functions if the context is not lost.
616       loseContextIfTime();
617       if (!contextLost_) {
618         //if (!checkResources(arguments)) {
619         //  glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
620         //  return;
621         //}
622         var result = f.apply(ctx, arguments);
623         return result;
624       }
625     };
626   }
627
628   function freeResources() {
629     for (var ii = 0; ii < resourceDb_.length; ++ii) {
630       var resource = resourceDb_[ii];
631       if (resource instanceof WebGLBuffer) {
632         unwrappedContext_.deleteBuffer(resource);
633       } else if (resource instanceof WebGLFramebuffer) {
634         unwrappedContext_.deleteFramebuffer(resource);
635       } else if (resource instanceof WebGLProgram) {
636         unwrappedContext_.deleteProgram(resource);
637       } else if (resource instanceof WebGLRenderbuffer) {
638         unwrappedContext_.deleteRenderbuffer(resource);
639       } else if (resource instanceof WebGLShader) {
640         unwrappedContext_.deleteShader(resource);
641       } else if (resource instanceof WebGLTexture) {
642         unwrappedContext_.deleteTexture(resource);
643       }
644     }
645   }
646
647   function makeWebGLContextEvent(statusMessage) {
648     return {
649       statusMessage: statusMessage,
650       preventDefault: function() {
651           canRestore_ = true;
652         }
653     };
654   }
655
656   return canvas;
657
658   function makeLostContextSimulatingContext(ctx) {
659     // copy all functions and properties to wrapper
660     for (var propertyName in ctx) {
661       if (typeof ctx[propertyName] == 'function') {
662          wrappedContext_[propertyName] = makeLostContextFunctionWrapper(
663              ctx, propertyName);
664        } else {
665          makePropertyWrapper(wrappedContext_, ctx, propertyName);
666        }
667     }
668
669     // Wrap a few functions specially.
670     wrappedContext_.getError = function() {
671       loseContextIfTime();
672       if (!contextLost_) {
673         var err;
674         while (err = unwrappedContext_.getError()) {
675           glErrorShadow_[err] = true;
676         }
677       }
678       for (var err in glErrorShadow_) {
679         if (glErrorShadow_[err]) {
680           delete glErrorShadow_[err];
681           return err;
682         }
683       }
684       return wrappedContext_.NO_ERROR;
685     };
686
687     var creationFunctions = [
688       "createBuffer",
689       "createFramebuffer",
690       "createProgram",
691       "createRenderbuffer",
692       "createShader",
693       "createTexture"
694     ];
695     for (var ii = 0; ii < creationFunctions.length; ++ii) {
696       var functionName = creationFunctions[ii];
697       wrappedContext_[functionName] = function(f) {
698         return function() {
699           loseContextIfTime();
700           if (contextLost_) {
701             return null;
702           }
703           var obj = f.apply(ctx, arguments);
704           obj.__webglDebugContextLostId__ = contextId_;
705           resourceDb_.push(obj);
706           return obj;
707         };
708       }(ctx[functionName]);
709     }
710
711     var functionsThatShouldReturnNull = [
712       "getActiveAttrib",
713       "getActiveUniform",
714       "getBufferParameter",
715       "getContextAttributes",
716       "getAttachedShaders",
717       "getFramebufferAttachmentParameter",
718       "getParameter",
719       "getProgramParameter",
720       "getProgramInfoLog",
721       "getRenderbufferParameter",
722       "getShaderParameter",
723       "getShaderInfoLog",
724       "getShaderSource",
725       "getTexParameter",
726       "getUniform",
727       "getUniformLocation",
728       "getVertexAttrib"
729     ];
730     for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) {
731       var functionName = functionsThatShouldReturnNull[ii];
732       wrappedContext_[functionName] = function(f) {
733         return function() {
734           loseContextIfTime();
735           if (contextLost_) {
736             return null;
737           }
738           return f.apply(ctx, arguments);
739         }
740       }(wrappedContext_[functionName]);
741     }
742
743     var isFunctions = [
744       "isBuffer",
745       "isEnabled",
746       "isFramebuffer",
747       "isProgram",
748       "isRenderbuffer",
749       "isShader",
750       "isTexture"
751     ];
752     for (var ii = 0; ii < isFunctions.length; ++ii) {
753       var functionName = isFunctions[ii];
754       wrappedContext_[functionName] = function(f) {
755         return function() {
756           loseContextIfTime();
757           if (contextLost_) {
758             return false;
759           }
760           return f.apply(ctx, arguments);
761         }
762       }(wrappedContext_[functionName]);
763     }
764
765     wrappedContext_.checkFramebufferStatus = function(f) {
766       return function() {
767         loseContextIfTime();
768         if (contextLost_) {
769           return wrappedContext_.FRAMEBUFFER_UNSUPPORTED;
770         }
771         return f.apply(ctx, arguments);
772       };
773     }(wrappedContext_.checkFramebufferStatus);
774
775     wrappedContext_.getAttribLocation = function(f) {
776       return function() {
777         loseContextIfTime();
778         if (contextLost_) {
779           return -1;
780         }
781         return f.apply(ctx, arguments);
782       };
783     }(wrappedContext_.getAttribLocation);
784
785     wrappedContext_.getVertexAttribOffset = function(f) {
786       return function() {
787         loseContextIfTime();
788         if (contextLost_) {
789           return 0;
790         }
791         return f.apply(ctx, arguments);
792       };
793     }(wrappedContext_.getVertexAttribOffset);
794
795     wrappedContext_.isContextLost = function() {
796       return contextLost_;
797     };
798
799     return wrappedContext_;
800   }
801 }
802
803 return {
804   /**
805    * Initializes this module. Safe to call more than once.
806    * @param {!WebGLRenderingContext} ctx A WebGL context. If
807    *    you have more than one context it doesn't matter which one
808    *    you pass in, it is only used to pull out constants.
809    */
810   'init': init,
811
812   /**
813    * Returns true or false if value matches any WebGL enum
814    * @param {*} value Value to check if it might be an enum.
815    * @return {boolean} True if value matches one of the WebGL defined enums
816    */
817   'mightBeEnum': mightBeEnum,
818
819   /**
820    * Gets an string version of an WebGL enum.
821    *
822    * Example:
823    *   WebGLDebugUtil.init(ctx);
824    *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());
825    *
826    * @param {number} value Value to return an enum for
827    * @return {string} The string version of the enum.
828    */
829   'glEnumToString': glEnumToString,
830
831   /**
832    * Converts the argument of a WebGL function to a string.
833    * Attempts to convert enum arguments to strings.
834    *
835    * Example:
836    *   WebGLDebugUtil.init(ctx);
837    *   var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 2, 0, gl.TEXTURE_2D);
838    *
839    * would return 'TEXTURE_2D'
840    *
841    * @param {string} functionName the name of the WebGL function.
842    * @param {number} numArgs The number of arguments
843    * @param {number} argumentIndx the index of the argument.
844    * @param {*} value The value of the argument.
845    * @return {string} The value as a string.
846    */
847   'glFunctionArgToString': glFunctionArgToString,
848
849   /**
850    * Converts the arguments of a WebGL function to a string.
851    * Attempts to convert enum arguments to strings.
852    *
853    * @param {string} functionName the name of the WebGL function.
854    * @param {number} args The arguments.
855    * @return {string} The arguments as a string.
856    */
857   'glFunctionArgsToString': glFunctionArgsToString,
858
859   /**
860    * Given a WebGL context returns a wrapped context that calls
861    * gl.getError after every command and calls a function if the
862    * result is not NO_ERROR.
863    *
864    * You can supply your own function if you want. For example, if you'd like
865    * an exception thrown on any GL error you could do this
866    *
867    *    function throwOnGLError(err, funcName, args) {
868    *      throw WebGLDebugUtils.glEnumToString(err) +
869    *            " was caused by call to " + funcName;
870    *    };
871    *
872    *    ctx = WebGLDebugUtils.makeDebugContext(
873    *        canvas.getContext("webgl"), throwOnGLError);
874    *
875    * @param {!WebGLRenderingContext} ctx The webgl context to wrap.
876    * @param {!function(err, funcName, args): void} opt_onErrorFunc The function
877    *     to call when gl.getError returns an error. If not specified the default
878    *     function calls console.log with a message.
879    * @param {!function(funcName, args): void} opt_onFunc The
880    *     function to call when each webgl function is called. You
881    *     can use this to log all calls for example.
882    */
883   'makeDebugContext': makeDebugContext,
884
885   /**
886    * Given a canvas element returns a wrapped canvas element that will
887    * simulate lost context. The canvas returned adds the following functions.
888    *
889    * loseContext:
890    *   simulates a lost context event.
891    *
892    * restoreContext:
893    *   simulates the context being restored.
894    *
895    * lostContextInNCalls:
896    *   loses the context after N gl calls.
897    *
898    * getNumCalls:
899    *   tells you how many gl calls there have been so far.
900    *
901    * setRestoreTimeout:
902    *   sets the number of milliseconds until the context is restored
903    *   after it has been lost. Defaults to 0. Pass -1 to prevent
904    *   automatic restoring.
905    *
906    * @param {!Canvas} canvas The canvas element to wrap.
907    */
908   'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas,
909
910   /**
911    * Resets a context to the initial state.
912    * @param {!WebGLRenderingContext} ctx The webgl context to
913    *     reset.
914    */
915   'resetToInitialState': resetToInitialState
916 };
917
918 }();
919