Upstream version 5.34.92.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
149 /**
150  * Map of numbers to names.
151  * @type {Object}
152  */
153 var glEnums = null;
154
155 /**
156  * Initializes this module. Safe to call more than once.
157  * @param {!WebGLRenderingContext} ctx A WebGL context. If
158  *    you have more than one context it doesn't matter which one
159  *    you pass in, it is only used to pull out constants.
160  */
161 function init(ctx) {
162   if (glEnums == null) {
163     glEnums = { };
164     for (var propertyName in ctx) {
165       if (typeof ctx[propertyName] == 'number') {
166         glEnums[ctx[propertyName]] = propertyName;
167       }
168     }
169   }
170 }
171
172 /**
173  * Checks the utils have been initialized.
174  */
175 function checkInit() {
176   if (glEnums == null) {
177     throw 'WebGLDebugUtils.init(ctx) not called';
178   }
179 }
180
181 /**
182  * Returns true or false if value matches any WebGL enum
183  * @param {*} value Value to check if it might be an enum.
184  * @return {boolean} True if value matches one of the WebGL defined enums
185  */
186 function mightBeEnum(value) {
187   checkInit();
188   return (glEnums[value] !== undefined);
189 }
190
191 /**
192  * Gets an string version of an WebGL enum.
193  *
194  * Example:
195  *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());
196  *
197  * @param {number} value Value to return an enum for
198  * @return {string} The string version of the enum.
199  */
200 function glEnumToString(value) {
201   checkInit();
202   var name = glEnums[value];
203   return (name !== undefined) ? ("gl." + name) :
204       ("/*UNKNOWN WebGL ENUM*/ 0x" + value.toString(16) + "");
205 }
206
207 /**
208  * Returns the string version of a WebGL argument.
209  * Attempts to convert enum arguments to strings.
210  * @param {string} functionName the name of the WebGL function.
211  * @param {number} numArgs the number of arguments passed to the function.
212  * @param {number} argumentIndx the index of the argument.
213  * @param {*} value The value of the argument.
214  * @return {string} The value as a string.
215  */
216 function glFunctionArgToString(functionName, numArgs, argumentIndex, value) {
217   var funcInfo = glValidEnumContexts[functionName];
218   if (funcInfo !== undefined) {
219     var funcInfo = funcInfo[numArgs];
220     if (funcInfo !== undefined) {
221       if (funcInfo[argumentIndex]) {
222         return glEnumToString(value);
223       }
224     }
225   }
226   if (value === null) {
227     return "null";
228   } else if (value === undefined) {
229     return "undefined";
230   } else {
231     return value.toString();
232   }
233 }
234
235 /**
236  * Converts the arguments of a WebGL function to a string.
237  * Attempts to convert enum arguments to strings.
238  *
239  * @param {string} functionName the name of the WebGL function.
240  * @param {number} args The arguments.
241  * @return {string} The arguments as a string.
242  */
243 function glFunctionArgsToString(functionName, args) {
244   // apparently we can't do args.join(",");
245   var argStr = "";
246   var numArgs = args.length;
247   for (var ii = 0; ii < numArgs; ++ii) {
248     argStr += ((ii == 0) ? '' : ', ') +
249         glFunctionArgToString(functionName, numArgs, ii, args[ii]);
250   }
251   return argStr;
252 };
253
254
255 function makePropertyWrapper(wrapper, original, propertyName) {
256   //log("wrap prop: " + propertyName);
257   wrapper.__defineGetter__(propertyName, function() {
258     return original[propertyName];
259   });
260   // TODO(gmane): this needs to handle properties that take more than
261   // one value?
262   wrapper.__defineSetter__(propertyName, function(value) {
263     //log("set: " + propertyName);
264     original[propertyName] = value;
265   });
266 }
267
268 // Makes a function that calls a function on another object.
269 function makeFunctionWrapper(original, functionName) {
270   //log("wrap fn: " + functionName);
271   var f = original[functionName];
272   return function() {
273     //log("call: " + functionName);
274     var result = f.apply(original, arguments);
275     return result;
276   };
277 }
278
279 /**
280  * Given a WebGL context returns a wrapped context that calls
281  * gl.getError after every command and calls a function if the
282  * result is not gl.NO_ERROR.
283  *
284  * @param {!WebGLRenderingContext} ctx The webgl context to
285  *        wrap.
286  * @param {!function(err, funcName, args): void} opt_onErrorFunc
287  *        The function to call when gl.getError returns an
288  *        error. If not specified the default function calls
289  *        console.log with a message.
290  * @param {!function(funcName, args): void} opt_onFunc The
291  *        function to call when each webgl function is called.
292  *        You can use this to log all calls for example.
293  */
294 function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc) {
295   init(ctx);
296   opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) {
297         // apparently we can't do args.join(",");
298         var argStr = "";
299         var numArgs = args.length;
300         for (var ii = 0; ii < numArgs; ++ii) {
301           argStr += ((ii == 0) ? '' : ', ') +
302               glFunctionArgToString(functionName, numArgs, ii, args[ii]);
303         }
304         error("WebGL error "+ glEnumToString(err) + " in "+ functionName +
305               "(" + argStr + ")");
306       };
307
308   // Holds booleans for each GL error so after we get the error ourselves
309   // we can still return it to the client app.
310   var glErrorShadow = { };
311
312   // Makes a function that calls a WebGL function and then calls getError.
313   function makeErrorWrapper(ctx, functionName) {
314     return function() {
315       if (opt_onFunc) {
316         opt_onFunc(functionName, arguments);
317       }
318       var result = ctx[functionName].apply(ctx, arguments);
319       var err = ctx.getError();
320       if (err != 0) {
321         glErrorShadow[err] = true;
322         opt_onErrorFunc(err, functionName, arguments);
323       }
324       return result;
325     };
326   }
327
328   // Make a an object that has a copy of every property of the WebGL context
329   // but wraps all functions.
330   var wrapper = {};
331   for (var propertyName in ctx) {
332     if (typeof ctx[propertyName] == 'function') {
333        wrapper[propertyName] = makeErrorWrapper(ctx, propertyName);
334      } else {
335        makePropertyWrapper(wrapper, ctx, propertyName);
336      }
337   }
338
339   // Override the getError function with one that returns our saved results.
340   wrapper.getError = function() {
341     for (var err in glErrorShadow) {
342       if (glErrorShadow.hasOwnProperty(err)) {
343         if (glErrorShadow[err]) {
344           glErrorShadow[err] = false;
345           return err;
346         }
347       }
348     }
349     return ctx.NO_ERROR;
350   };
351
352   return wrapper;
353 }
354
355 function resetToInitialState(ctx) {
356   var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS);
357   var tmp = ctx.createBuffer();
358   ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp);
359   for (var ii = 0; ii < numAttribs; ++ii) {
360     ctx.disableVertexAttribArray(ii);
361     ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0);
362     ctx.vertexAttrib1f(ii, 0);
363   }
364   ctx.deleteBuffer(tmp);
365
366   var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS);
367   for (var ii = 0; ii < numTextureUnits; ++ii) {
368     ctx.activeTexture(ctx.TEXTURE0 + ii);
369     ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null);
370     ctx.bindTexture(ctx.TEXTURE_2D, null);
371   }
372
373   ctx.activeTexture(ctx.TEXTURE0);
374   ctx.useProgram(null);
375   ctx.bindBuffer(ctx.ARRAY_BUFFER, null);
376   ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null);
377   ctx.bindFramebuffer(ctx.FRAMEBUFFER, null);
378   ctx.bindRenderbuffer(ctx.RENDERBUFFER, null);
379   ctx.disable(ctx.BLEND);
380   ctx.disable(ctx.CULL_FACE);
381   ctx.disable(ctx.DEPTH_TEST);
382   ctx.disable(ctx.DITHER);
383   ctx.disable(ctx.SCISSOR_TEST);
384   ctx.blendColor(0, 0, 0, 0);
385   ctx.blendEquation(ctx.FUNC_ADD);
386   ctx.blendFunc(ctx.ONE, ctx.ZERO);
387   ctx.clearColor(0, 0, 0, 0);
388   ctx.clearDepth(1);
389   ctx.clearStencil(-1);
390   ctx.colorMask(true, true, true, true);
391   ctx.cullFace(ctx.BACK);
392   ctx.depthFunc(ctx.LESS);
393   ctx.depthMask(true);
394   ctx.depthRange(0, 1);
395   ctx.frontFace(ctx.CCW);
396   ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE);
397   ctx.lineWidth(1);
398   ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4);
399   ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4);
400   ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false);
401   ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
402   // TODO: Delete this IF.
403   if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) {
404     ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL);
405   }
406   ctx.polygonOffset(0, 0);
407   ctx.sampleCoverage(1, false);
408   ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height);
409   ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF);
410   ctx.stencilMask(0xFFFFFFFF);
411   ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP);
412   ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height);
413   ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
414
415   // TODO: This should NOT be needed but Firefox fails with 'hint'
416   while(ctx.getError());
417 }
418
419 function makeLostContextSimulatingCanvas(canvas) {
420   var unwrappedContext_;
421   var wrappedContext_;
422   var onLost_ = [];
423   var onRestored_ = [];
424   var wrappedContext_ = {};
425   var contextId_ = 1;
426   var contextLost_ = false;
427   var resourceId_ = 0;
428   var resourceDb_ = [];
429   var numCallsToLoseContext_ = 0;
430   var numCalls_ = 0;
431   var canRestore_ = false;
432   var restoreTimeout_ = 0;
433
434   // Holds booleans for each GL error so can simulate errors.
435   var glErrorShadow_ = { };
436
437   canvas.getContext = function(f) {
438     return function() {
439       var ctx = f.apply(canvas, arguments);
440       // Did we get a context and is it a WebGL context?
441       if (ctx instanceof WebGLRenderingContext) {
442         if (ctx != unwrappedContext_) {
443           if (unwrappedContext_) {
444             throw "got different context"
445           }
446           unwrappedContext_ = ctx;
447           wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_);
448         }
449         return wrappedContext_;
450       }
451       return ctx;
452     }
453   }(canvas.getContext);
454
455   function wrapEvent(listener) {
456     if (typeof(listener) == "function") {
457       return listener;
458     } else {
459       return function(info) {
460         listener.handleEvent(info);
461       }
462     }
463   }
464
465   var addOnContextLostListener = function(listener) {
466     onLost_.push(wrapEvent(listener));
467   };
468
469   var addOnContextRestoredListener = function(listener) {
470     onRestored_.push(wrapEvent(listener));
471   };
472
473
474   function wrapAddEventListener(canvas) {
475     var f = canvas.addEventListener;
476     canvas.addEventListener = function(type, listener, bubble) {
477       switch (type) {
478         case 'webglcontextlost':
479           addOnContextLostListener(listener);
480           break;
481         case 'webglcontextrestored':
482           addOnContextRestoredListener(listener);
483           break;
484         default:
485           f.apply(canvas, arguments);
486       }
487     };
488   }
489
490   wrapAddEventListener(canvas);
491
492   canvas.loseContext = function() {
493     if (!contextLost_) {
494       contextLost_ = true;
495       numCallsToLoseContext_ = 0;
496       ++contextId_;
497       while (unwrappedContext_.getError());
498       clearErrors();
499       glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true;
500       var event = makeWebGLContextEvent("context lost");
501       var callbacks = onLost_.slice();
502       setTimeout(function() {
503           //log("numCallbacks:" + callbacks.length);
504           for (var ii = 0; ii < callbacks.length; ++ii) {
505             //log("calling callback:" + ii);
506             callbacks[ii](event);
507           }
508           if (restoreTimeout_ >= 0) {
509             setTimeout(function() {
510                 canvas.restoreContext();
511               }, restoreTimeout_);
512           }
513         }, 0);
514     }
515   };
516
517   canvas.restoreContext = function() {
518     if (contextLost_) {
519       if (onRestored_.length) {
520         setTimeout(function() {
521             if (!canRestore_) {
522               throw "can not restore. webglcontestlost listener did not call event.preventDefault";
523             }
524             freeResources();
525             resetToInitialState(unwrappedContext_);
526             contextLost_ = false;
527             numCalls_ = 0;
528             canRestore_ = false;
529             var callbacks = onRestored_.slice();
530             var event = makeWebGLContextEvent("context restored");
531             for (var ii = 0; ii < callbacks.length; ++ii) {
532               callbacks[ii](event);
533             }
534           }, 0);
535       }
536     }
537   };
538
539   canvas.loseContextInNCalls = function(numCalls) {
540     if (contextLost_) {
541       throw "You can not ask a lost contet to be lost";
542     }
543     numCallsToLoseContext_ = numCalls_ + numCalls;
544   };
545
546   canvas.getNumCalls = function() {
547     return numCalls_;
548   };
549
550   canvas.setRestoreTimeout = function(timeout) {
551     restoreTimeout_ = timeout;
552   };
553
554   function isWebGLObject(obj) {
555     //return false;
556     return (obj instanceof WebGLBuffer ||
557             obj instanceof WebGLFramebuffer ||
558             obj instanceof WebGLProgram ||
559             obj instanceof WebGLRenderbuffer ||
560             obj instanceof WebGLShader ||
561             obj instanceof WebGLTexture);
562   }
563
564   function checkResources(args) {
565     for (var ii = 0; ii < args.length; ++ii) {
566       var arg = args[ii];
567       if (isWebGLObject(arg)) {
568         return arg.__webglDebugContextLostId__ == contextId_;
569       }
570     }
571     return true;
572   }
573
574   function clearErrors() {
575     var k = Object.keys(glErrorShadow_);
576     for (var ii = 0; ii < k.length; ++ii) {
577       delete glErrorShadow_[k];
578     }
579   }
580
581   function loseContextIfTime() {
582     ++numCalls_;
583     if (!contextLost_) {
584       if (numCallsToLoseContext_ == numCalls_) {
585         canvas.loseContext();
586       }
587     }
588   }
589
590   // Makes a function that simulates WebGL when out of context.
591   function makeLostContextFunctionWrapper(ctx, functionName) {
592     var f = ctx[functionName];
593     return function() {
594       // log("calling:" + functionName);
595       // Only call the functions if the context is not lost.
596       loseContextIfTime();
597       if (!contextLost_) {
598         //if (!checkResources(arguments)) {
599         //  glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true;
600         //  return;
601         //}
602         var result = f.apply(ctx, arguments);
603         return result;
604       }
605     };
606   }
607
608   function freeResources() {
609     for (var ii = 0; ii < resourceDb_.length; ++ii) {
610       var resource = resourceDb_[ii];
611       if (resource instanceof WebGLBuffer) {
612         unwrappedContext_.deleteBuffer(resource);
613       } else if (resource instanceof WebGLFramebuffer) {
614         unwrappedContext_.deleteFramebuffer(resource);
615       } else if (resource instanceof WebGLProgram) {
616         unwrappedContext_.deleteProgram(resource);
617       } else if (resource instanceof WebGLRenderbuffer) {
618         unwrappedContext_.deleteRenderbuffer(resource);
619       } else if (resource instanceof WebGLShader) {
620         unwrappedContext_.deleteShader(resource);
621       } else if (resource instanceof WebGLTexture) {
622         unwrappedContext_.deleteTexture(resource);
623       }
624     }
625   }
626
627   function makeWebGLContextEvent(statusMessage) {
628     return {
629       statusMessage: statusMessage,
630       preventDefault: function() {
631           canRestore_ = true;
632         }
633     };
634   }
635
636   return canvas;
637
638   function makeLostContextSimulatingContext(ctx) {
639     // copy all functions and properties to wrapper
640     for (var propertyName in ctx) {
641       if (typeof ctx[propertyName] == 'function') {
642          wrappedContext_[propertyName] = makeLostContextFunctionWrapper(
643              ctx, propertyName);
644        } else {
645          makePropertyWrapper(wrappedContext_, ctx, propertyName);
646        }
647     }
648
649     // Wrap a few functions specially.
650     wrappedContext_.getError = function() {
651       loseContextIfTime();
652       if (!contextLost_) {
653         var err;
654         while (err = unwrappedContext_.getError()) {
655           glErrorShadow_[err] = true;
656         }
657       }
658       for (var err in glErrorShadow_) {
659         if (glErrorShadow_[err]) {
660           delete glErrorShadow_[err];
661           return err;
662         }
663       }
664       return wrappedContext_.NO_ERROR;
665     };
666
667     var creationFunctions = [
668       "createBuffer",
669       "createFramebuffer",
670       "createProgram",
671       "createRenderbuffer",
672       "createShader",
673       "createTexture"
674     ];
675     for (var ii = 0; ii < creationFunctions.length; ++ii) {
676       var functionName = creationFunctions[ii];
677       wrappedContext_[functionName] = function(f) {
678         return function() {
679           loseContextIfTime();
680           if (contextLost_) {
681             return null;
682           }
683           var obj = f.apply(ctx, arguments);
684           obj.__webglDebugContextLostId__ = contextId_;
685           resourceDb_.push(obj);
686           return obj;
687         };
688       }(ctx[functionName]);
689     }
690
691     var functionsThatShouldReturnNull = [
692       "getActiveAttrib",
693       "getActiveUniform",
694       "getBufferParameter",
695       "getContextAttributes",
696       "getAttachedShaders",
697       "getFramebufferAttachmentParameter",
698       "getParameter",
699       "getProgramParameter",
700       "getProgramInfoLog",
701       "getRenderbufferParameter",
702       "getShaderParameter",
703       "getShaderInfoLog",
704       "getShaderSource",
705       "getTexParameter",
706       "getUniform",
707       "getUniformLocation",
708       "getVertexAttrib"
709     ];
710     for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) {
711       var functionName = functionsThatShouldReturnNull[ii];
712       wrappedContext_[functionName] = function(f) {
713         return function() {
714           loseContextIfTime();
715           if (contextLost_) {
716             return null;
717           }
718           return f.apply(ctx, arguments);
719         }
720       }(wrappedContext_[functionName]);
721     }
722
723     var isFunctions = [
724       "isBuffer",
725       "isEnabled",
726       "isFramebuffer",
727       "isProgram",
728       "isRenderbuffer",
729       "isShader",
730       "isTexture"
731     ];
732     for (var ii = 0; ii < isFunctions.length; ++ii) {
733       var functionName = isFunctions[ii];
734       wrappedContext_[functionName] = function(f) {
735         return function() {
736           loseContextIfTime();
737           if (contextLost_) {
738             return false;
739           }
740           return f.apply(ctx, arguments);
741         }
742       }(wrappedContext_[functionName]);
743     }
744
745     wrappedContext_.checkFramebufferStatus = function(f) {
746       return function() {
747         loseContextIfTime();
748         if (contextLost_) {
749           return wrappedContext_.FRAMEBUFFER_UNSUPPORTED;
750         }
751         return f.apply(ctx, arguments);
752       };
753     }(wrappedContext_.checkFramebufferStatus);
754
755     wrappedContext_.getAttribLocation = function(f) {
756       return function() {
757         loseContextIfTime();
758         if (contextLost_) {
759           return -1;
760         }
761         return f.apply(ctx, arguments);
762       };
763     }(wrappedContext_.getAttribLocation);
764
765     wrappedContext_.getVertexAttribOffset = function(f) {
766       return function() {
767         loseContextIfTime();
768         if (contextLost_) {
769           return 0;
770         }
771         return f.apply(ctx, arguments);
772       };
773     }(wrappedContext_.getVertexAttribOffset);
774
775     wrappedContext_.isContextLost = function() {
776       return contextLost_;
777     };
778
779     return wrappedContext_;
780   }
781 }
782
783 return {
784     /**
785      * Initializes this module. Safe to call more than once.
786      * @param {!WebGLRenderingContext} ctx A WebGL context. If
787     }
788    *    you have more than one context it doesn't matter which one
789    *    you pass in, it is only used to pull out constants.
790    */
791   'init': init,
792
793   /**
794    * Returns true or false if value matches any WebGL enum
795    * @param {*} value Value to check if it might be an enum.
796    * @return {boolean} True if value matches one of the WebGL defined enums
797    */
798   'mightBeEnum': mightBeEnum,
799
800   /**
801    * Gets an string version of an WebGL enum.
802    *
803    * Example:
804    *   WebGLDebugUtil.init(ctx);
805    *   var str = WebGLDebugUtil.glEnumToString(ctx.getError());
806    *
807    * @param {number} value Value to return an enum for
808    * @return {string} The string version of the enum.
809    */
810   'glEnumToString': glEnumToString,
811
812   /**
813    * Converts the argument of a WebGL function to a string.
814    * Attempts to convert enum arguments to strings.
815    *
816    * Example:
817    *   WebGLDebugUtil.init(ctx);
818    *   var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 2, 0, gl.TEXTURE_2D);
819    *
820    * would return 'TEXTURE_2D'
821    *
822    * @param {string} functionName the name of the WebGL function.
823    * @param {number} numArgs The number of arguments
824    * @param {number} argumentIndx the index of the argument.
825    * @param {*} value The value of the argument.
826    * @return {string} The value as a string.
827    */
828   'glFunctionArgToString': glFunctionArgToString,
829
830   /**
831    * Converts the arguments of a WebGL function to a string.
832    * Attempts to convert enum arguments to strings.
833    *
834    * @param {string} functionName the name of the WebGL function.
835    * @param {number} args The arguments.
836    * @return {string} The arguments as a string.
837    */
838   'glFunctionArgsToString': glFunctionArgsToString,
839
840   /**
841    * Given a WebGL context returns a wrapped context that calls
842    * gl.getError after every command and calls a function if the
843    * result is not NO_ERROR.
844    *
845    * You can supply your own function if you want. For example, if you'd like
846    * an exception thrown on any GL error you could do this
847    *
848    *    function throwOnGLError(err, funcName, args) {
849    *      throw WebGLDebugUtils.glEnumToString(err) +
850    *            " was caused by call to " + funcName;
851    *    };
852    *
853    *    ctx = WebGLDebugUtils.makeDebugContext(
854    *        canvas.getContext("webgl"), throwOnGLError);
855    *
856    * @param {!WebGLRenderingContext} ctx The webgl context to wrap.
857    * @param {!function(err, funcName, args): void} opt_onErrorFunc The function
858    *     to call when gl.getError returns an error. If not specified the default
859    *     function calls console.log with a message.
860    * @param {!function(funcName, args): void} opt_onFunc The
861    *     function to call when each webgl function is called. You
862    *     can use this to log all calls for example.
863    */
864   'makeDebugContext': makeDebugContext,
865
866   /**
867    * Given a canvas element returns a wrapped canvas element that will
868    * simulate lost context. The canvas returned adds the following functions.
869    *
870    * loseContext:
871    *   simulates a lost context event.
872    *
873    * restoreContext:
874    *   simulates the context being restored.
875    *
876    * lostContextInNCalls:
877    *   loses the context after N gl calls.
878    *
879    * getNumCalls:
880    *   tells you how many gl calls there have been so far.
881    *
882    * setRestoreTimeout:
883    *   sets the number of milliseconds until the context is restored
884    *   after it has been lost. Defaults to 0. Pass -1 to prevent
885    *   automatic restoring.
886    *
887    * @param {!Canvas} canvas The canvas element to wrap.
888    */
889   'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas,
890
891   /**
892    * Resets a context to the initial state.
893    * @param {!WebGLRenderingContext} ctx The webgl context to
894    *     reset.
895    */
896   'resetToInitialState': resetToInitialState
897 };
898
899 }();
900