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