Created
November 7, 2012 15:47
-
-
Save kapadia/4032377 to your computer and use it in GitHub Desktop.
Safari WebGL Issues
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<script src="webgl-debug.js"></script> | |
<script src="webgl-utils.js"></script> | |
<script src="main.js"></script> | |
<script id="2d-vertex-shader" type="x-shader/x-vertex"> | |
attribute vec2 a_position; | |
void main() { | |
gl_Position = vec4(a_position, 0, 1); | |
} | |
</script> | |
<script id="2d-fragment-shader" type="x-shader/x-fragment"> | |
precision mediump float; | |
uniform vec2 u_resolution; | |
uniform sampler2D u_tex; | |
void main() { | |
vec2 texCoord = gl_FragCoord.xy / u_resolution; | |
vec4 pixel_v = texture2D(u_tex, texCoord); | |
float value = pixel_v[0]; | |
float min = 0.0; | |
float max = 10.0; | |
float range = max - min; | |
float pixel = (value - min) / range; | |
gl_FragColor = vec4(vec3(pixel, pixel, pixel), 1.0); | |
} | |
</script> | |
</head> | |
<body onload='main()'> | |
<canvas id="canvas" width="400" height="300"></canvas> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function main() { | |
var canvas = document.getElementById("canvas"); | |
var gl = getWebGLContext(canvas); | |
window.gl = gl; | |
if (!gl) { | |
alert("no WebGL"); | |
return; | |
} | |
var ext = gl.getExtension("OES_texture_float"); | |
if (!ext) { | |
alert("no OES_texture_float"); | |
return; | |
} | |
vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader"); | |
fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader"); | |
program = createProgram(gl, [vertexShader, fragmentShader]); | |
gl.useProgram(program); | |
var positionLocation = gl.getAttribLocation(program, "a_position"); | |
var resolutionLocation = gl.getUniformLocation(program, "u_resolution"); | |
gl.uniform2f(resolutionLocation, canvas.width, canvas.height); | |
var buffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ | |
-1, -1, 1, -1, -1, 1, | |
-1, 1, 1, -1, 1, 1]), gl.STATIC_DRAW); | |
gl.enableVertexAttribArray(positionLocation); | |
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); | |
var tex = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, tex); | |
var width = 64; | |
var height = 64; | |
var pixels = new Float32Array(width * height); | |
for (var y = 0; y < height; ++y) { | |
for (var x = 0; x < width; ++x) { | |
var offset = y * width + x; | |
pixels[offset] = 10 * (x / width + y / height) * 0.5; | |
} | |
} | |
gl.texImage2D( | |
gl.TEXTURE_2D, 0, | |
gl.LUMINANCE, width, height, 0, | |
gl.LUMINANCE, gl.FLOAT, pixels); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
gl.drawArrays(gl.TRIANGLES, 0, 6); | |
console.log(gl.getError()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
** Copyright (c) 2012 The Khronos Group Inc. | |
** | |
** Permission is hereby granted, free of charge, to any person obtaining a | |
** copy of this software and/or associated documentation files (the | |
** "Materials"), to deal in the Materials without restriction, including | |
** without limitation the rights to use, copy, modify, merge, publish, | |
** distribute, sublicense, and/or sell copies of the Materials, and to | |
** permit persons to whom the Materials are furnished to do so, subject to | |
** the following conditions: | |
** | |
** The above copyright notice and this permission notice shall be included | |
** in all copies or substantial portions of the Materials. | |
** | |
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. | |
*/ | |
// Various functions for helping debug WebGL apps. | |
WebGLDebugUtils = function() { | |
/** | |
* Wrapped logging function. | |
* @param {string} msg Message to log. | |
*/ | |
var log = function(msg) { | |
if (window.console && window.console.log) { | |
window.console.log(msg); | |
} | |
}; | |
/** | |
* Wrapped error logging function. | |
* @param {string} msg Message to log. | |
*/ | |
var error = function(msg) { | |
if (window.console && window.console.error) { | |
window.console.error(msg); | |
} else { | |
log(msg); | |
} | |
}; | |
/** | |
* Which arguements are enums. | |
* @type {!Object.<number, string>} | |
*/ | |
var glValidEnumContexts = { | |
// Generic setters and getters | |
'enable': { 0:true }, | |
'disable': { 0:true }, | |
'getParameter': { 0:true }, | |
// Rendering | |
'drawArrays': { 0:true }, | |
'drawElements': { 0:true, 2:true }, | |
// Shaders | |
'createShader': { 0:true }, | |
'getShaderParameter': { 1:true }, | |
'getProgramParameter': { 1:true }, | |
// Vertex attributes | |
'getVertexAttrib': { 1:true }, | |
'vertexAttribPointer': { 2:true }, | |
// Textures | |
'bindTexture': { 0:true }, | |
'activeTexture': { 0:true }, | |
'getTexParameter': { 0:true, 1:true }, | |
'texParameterf': { 0:true, 1:true }, | |
'texParameteri': { 0:true, 1:true, 2:true }, | |
'texImage2D': { 0:true, 2:true, 6:true, 7:true }, | |
'texSubImage2D': { 0:true, 6:true, 7:true }, | |
'copyTexImage2D': { 0:true, 2:true }, | |
'copyTexSubImage2D': { 0:true }, | |
'generateMipmap': { 0:true }, | |
// Buffer objects | |
'bindBuffer': { 0:true }, | |
'bufferData': { 0:true, 2:true }, | |
'bufferSubData': { 0:true }, | |
'getBufferParameter': { 0:true, 1:true }, | |
// Renderbuffers and framebuffers | |
'pixelStorei': { 0:true, 1:true }, | |
'readPixels': { 4:true, 5:true }, | |
'bindRenderbuffer': { 0:true }, | |
'bindFramebuffer': { 0:true }, | |
'checkFramebufferStatus': { 0:true }, | |
'framebufferRenderbuffer': { 0:true, 1:true, 2:true }, | |
'framebufferTexture2D': { 0:true, 1:true, 2:true }, | |
'getFramebufferAttachmentParameter': { 0:true, 1:true, 2:true }, | |
'getRenderbufferParameter': { 0:true, 1:true }, | |
'renderbufferStorage': { 0:true, 1:true }, | |
// Frame buffer operations (clear, blend, depth test, stencil) | |
'clear': { 0:true }, | |
'depthFunc': { 0:true }, | |
'blendFunc': { 0:true, 1:true }, | |
'blendFuncSeparate': { 0:true, 1:true, 2:true, 3:true }, | |
'blendEquation': { 0:true }, | |
'blendEquationSeparate': { 0:true, 1:true }, | |
'stencilFunc': { 0:true }, | |
'stencilFuncSeparate': { 0:true, 1:true }, | |
'stencilMaskSeparate': { 0:true }, | |
'stencilOp': { 0:true, 1:true, 2:true }, | |
'stencilOpSeparate': { 0:true, 1:true, 2:true, 3:true }, | |
// Culling | |
'cullFace': { 0:true }, | |
'frontFace': { 0:true }, | |
}; | |
/** | |
* Map of numbers to names. | |
* @type {Object} | |
*/ | |
var glEnums = null; | |
/** | |
* Initializes this module. Safe to call more than once. | |
* @param {!WebGLRenderingContext} ctx A WebGL context. If | |
* you have more than one context it doesn't matter which one | |
* you pass in, it is only used to pull out constants. | |
*/ | |
function init(ctx) { | |
if (glEnums == null) { | |
glEnums = { }; | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'number') { | |
glEnums[ctx[propertyName]] = propertyName; | |
} | |
} | |
} | |
} | |
/** | |
* Checks the utils have been initialized. | |
*/ | |
function checkInit() { | |
if (glEnums == null) { | |
throw 'WebGLDebugUtils.init(ctx) not called'; | |
} | |
} | |
/** | |
* Returns true or false if value matches any WebGL enum | |
* @param {*} value Value to check if it might be an enum. | |
* @return {boolean} True if value matches one of the WebGL defined enums | |
*/ | |
function mightBeEnum(value) { | |
checkInit(); | |
return (glEnums[value] !== undefined); | |
} | |
/** | |
* Gets an string version of an WebGL enum. | |
* | |
* Example: | |
* var str = WebGLDebugUtil.glEnumToString(ctx.getError()); | |
* | |
* @param {number} value Value to return an enum for | |
* @return {string} The string version of the enum. | |
*/ | |
function glEnumToString(value) { | |
checkInit(); | |
var name = glEnums[value]; | |
return (name !== undefined) ? name : | |
("*UNKNOWN WebGL ENUM (0x" + value.toString(16) + ")"); | |
} | |
/** | |
* Returns the string version of a WebGL argument. | |
* Attempts to convert enum arguments to strings. | |
* @param {string} functionName the name of the WebGL function. | |
* @param {number} argumentIndx the index of the argument. | |
* @param {*} value The value of the argument. | |
* @return {string} The value as a string. | |
*/ | |
function glFunctionArgToString(functionName, argumentIndex, value) { | |
var funcInfo = glValidEnumContexts[functionName]; | |
if (funcInfo !== undefined) { | |
if (funcInfo[argumentIndex]) { | |
return glEnumToString(value); | |
} | |
} | |
if (value === null) { | |
return "null"; | |
} else if (value === undefined) { | |
return "undefined"; | |
} else { | |
return value.toString(); | |
} | |
} | |
/** | |
* Converts the arguments of a WebGL function to a string. | |
* Attempts to convert enum arguments to strings. | |
* | |
* @param {string} functionName the name of the WebGL function. | |
* @param {number} args The arguments. | |
* @return {string} The arguments as a string. | |
*/ | |
function glFunctionArgsToString(functionName, args) { | |
// apparently we can't do args.join(","); | |
var argStr = ""; | |
for (var ii = 0; ii < args.length; ++ii) { | |
argStr += ((ii == 0) ? '' : ', ') + | |
glFunctionArgToString(functionName, ii, args[ii]); | |
} | |
return argStr; | |
}; | |
function makePropertyWrapper(wrapper, original, propertyName) { | |
//log("wrap prop: " + propertyName); | |
wrapper.__defineGetter__(propertyName, function() { | |
return original[propertyName]; | |
}); | |
// TODO(gmane): this needs to handle properties that take more than | |
// one value? | |
wrapper.__defineSetter__(propertyName, function(value) { | |
//log("set: " + propertyName); | |
original[propertyName] = value; | |
}); | |
} | |
// Makes a function that calls a function on another object. | |
function makeFunctionWrapper(original, functionName) { | |
//log("wrap fn: " + functionName); | |
var f = original[functionName]; | |
return function() { | |
//log("call: " + functionName); | |
var result = f.apply(original, arguments); | |
return result; | |
}; | |
} | |
/** | |
* Given a WebGL context returns a wrapped context that calls | |
* gl.getError after every command and calls a function if the | |
* result is not gl.NO_ERROR. | |
* | |
* @param {!WebGLRenderingContext} ctx The webgl context to | |
* wrap. | |
* @param {!function(err, funcName, args): void} opt_onErrorFunc | |
* The function to call when gl.getError returns an | |
* error. If not specified the default function calls | |
* console.log with a message. | |
* @param {!function(funcName, args): void} opt_onFunc The | |
* function to call when each webgl function is called. | |
* You can use this to log all calls for example. | |
*/ | |
function makeDebugContext(ctx, opt_onErrorFunc, opt_onFunc) { | |
init(ctx); | |
opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { | |
// apparently we can't do args.join(","); | |
var argStr = ""; | |
for (var ii = 0; ii < args.length; ++ii) { | |
argStr += ((ii == 0) ? '' : ', ') + | |
glFunctionArgToString(functionName, ii, args[ii]); | |
} | |
error("WebGL error "+ glEnumToString(err) + " in "+ functionName + | |
"(" + argStr + ")"); | |
}; | |
// Holds booleans for each GL error so after we get the error ourselves | |
// we can still return it to the client app. | |
var glErrorShadow = { }; | |
// Makes a function that calls a WebGL function and then calls getError. | |
function makeErrorWrapper(ctx, functionName) { | |
return function() { | |
if (opt_onFunc) { | |
opt_onFunc(functionName, arguments); | |
} | |
var result = ctx[functionName].apply(ctx, arguments); | |
var err = ctx.getError(); | |
if (err != 0) { | |
glErrorShadow[err] = true; | |
opt_onErrorFunc(err, functionName, arguments); | |
} | |
return result; | |
}; | |
} | |
// Make a an object that has a copy of every property of the WebGL context | |
// but wraps all functions. | |
var wrapper = {}; | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'function') { | |
wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); | |
} else { | |
makePropertyWrapper(wrapper, ctx, propertyName); | |
} | |
} | |
// Override the getError function with one that returns our saved results. | |
wrapper.getError = function() { | |
for (var err in glErrorShadow) { | |
if (glErrorShadow.hasOwnProperty(err)) { | |
if (glErrorShadow[err]) { | |
glErrorShadow[err] = false; | |
return err; | |
} | |
} | |
} | |
return ctx.NO_ERROR; | |
}; | |
return wrapper; | |
} | |
function resetToInitialState(ctx) { | |
var numAttribs = ctx.getParameter(ctx.MAX_VERTEX_ATTRIBS); | |
var tmp = ctx.createBuffer(); | |
ctx.bindBuffer(ctx.ARRAY_BUFFER, tmp); | |
for (var ii = 0; ii < numAttribs; ++ii) { | |
ctx.disableVertexAttribArray(ii); | |
ctx.vertexAttribPointer(ii, 4, ctx.FLOAT, false, 0, 0); | |
ctx.vertexAttrib1f(ii, 0); | |
} | |
ctx.deleteBuffer(tmp); | |
var numTextureUnits = ctx.getParameter(ctx.MAX_TEXTURE_IMAGE_UNITS); | |
for (var ii = 0; ii < numTextureUnits; ++ii) { | |
ctx.activeTexture(ctx.TEXTURE0 + ii); | |
ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, null); | |
ctx.bindTexture(ctx.TEXTURE_2D, null); | |
} | |
ctx.activeTexture(ctx.TEXTURE0); | |
ctx.useProgram(null); | |
ctx.bindBuffer(ctx.ARRAY_BUFFER, null); | |
ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, null); | |
ctx.bindFramebuffer(ctx.FRAMEBUFFER, null); | |
ctx.bindRenderbuffer(ctx.RENDERBUFFER, null); | |
ctx.disable(ctx.BLEND); | |
ctx.disable(ctx.CULL_FACE); | |
ctx.disable(ctx.DEPTH_TEST); | |
ctx.disable(ctx.DITHER); | |
ctx.disable(ctx.SCISSOR_TEST); | |
ctx.blendColor(0, 0, 0, 0); | |
ctx.blendEquation(ctx.FUNC_ADD); | |
ctx.blendFunc(ctx.ONE, ctx.ZERO); | |
ctx.clearColor(0, 0, 0, 0); | |
ctx.clearDepth(1); | |
ctx.clearStencil(-1); | |
ctx.colorMask(true, true, true, true); | |
ctx.cullFace(ctx.BACK); | |
ctx.depthFunc(ctx.LESS); | |
ctx.depthMask(true); | |
ctx.depthRange(0, 1); | |
ctx.frontFace(ctx.CCW); | |
ctx.hint(ctx.GENERATE_MIPMAP_HINT, ctx.DONT_CARE); | |
ctx.lineWidth(1); | |
ctx.pixelStorei(ctx.PACK_ALIGNMENT, 4); | |
ctx.pixelStorei(ctx.UNPACK_ALIGNMENT, 4); | |
ctx.pixelStorei(ctx.UNPACK_FLIP_Y_WEBGL, false); | |
ctx.pixelStorei(ctx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); | |
// TODO: Delete this IF. | |
if (ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL) { | |
ctx.pixelStorei(ctx.UNPACK_COLORSPACE_CONVERSION_WEBGL, ctx.BROWSER_DEFAULT_WEBGL); | |
} | |
ctx.polygonOffset(0, 0); | |
ctx.sampleCoverage(1, false); | |
ctx.scissor(0, 0, ctx.canvas.width, ctx.canvas.height); | |
ctx.stencilFunc(ctx.ALWAYS, 0, 0xFFFFFFFF); | |
ctx.stencilMask(0xFFFFFFFF); | |
ctx.stencilOp(ctx.KEEP, ctx.KEEP, ctx.KEEP); | |
ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); | |
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT); | |
// TODO: This should NOT be needed but Firefox fails with 'hint' | |
while(ctx.getError()); | |
} | |
function makeLostContextSimulatingCanvas(canvas) { | |
var unwrappedContext_; | |
var wrappedContext_; | |
var onLost_ = []; | |
var onRestored_ = []; | |
var wrappedContext_ = {}; | |
var contextId_ = 1; | |
var contextLost_ = false; | |
var resourceId_ = 0; | |
var resourceDb_ = []; | |
var numCallsToLoseContext_ = 0; | |
var numCalls_ = 0; | |
var canRestore_ = false; | |
var restoreTimeout_ = 0; | |
// Holds booleans for each GL error so can simulate errors. | |
var glErrorShadow_ = { }; | |
canvas.getContext = function(f) { | |
return function() { | |
var ctx = f.apply(canvas, arguments); | |
// Did we get a context and is it a WebGL context? | |
if (ctx instanceof WebGLRenderingContext) { | |
if (ctx != unwrappedContext_) { | |
if (unwrappedContext_) { | |
throw "got different context" | |
} | |
unwrappedContext_ = ctx; | |
wrappedContext_ = makeLostContextSimulatingContext(unwrappedContext_); | |
} | |
return wrappedContext_; | |
} | |
return ctx; | |
} | |
}(canvas.getContext); | |
function wrapEvent(listener) { | |
if (typeof(listener) == "function") { | |
return listener; | |
} else { | |
return function(info) { | |
listener.handleEvent(info); | |
} | |
} | |
} | |
var addOnContextLostListener = function(listener) { | |
onLost_.push(wrapEvent(listener)); | |
}; | |
var addOnContextRestoredListener = function(listener) { | |
onRestored_.push(wrapEvent(listener)); | |
}; | |
function wrapAddEventListener(canvas) { | |
var f = canvas.addEventListener; | |
canvas.addEventListener = function(type, listener, bubble) { | |
switch (type) { | |
case 'webglcontextlost': | |
addOnContextLostListener(listener); | |
break; | |
case 'webglcontextrestored': | |
addOnContextRestoredListener(listener); | |
break; | |
default: | |
f.apply(canvas, arguments); | |
} | |
}; | |
} | |
wrapAddEventListener(canvas); | |
canvas.loseContext = function() { | |
if (!contextLost_) { | |
contextLost_ = true; | |
numCallsToLoseContext_ = 0; | |
++contextId_; | |
while (unwrappedContext_.getError()); | |
clearErrors(); | |
glErrorShadow_[unwrappedContext_.CONTEXT_LOST_WEBGL] = true; | |
var event = makeWebGLContextEvent("context lost"); | |
var callbacks = onLost_.slice(); | |
setTimeout(function() { | |
//log("numCallbacks:" + callbacks.length); | |
for (var ii = 0; ii < callbacks.length; ++ii) { | |
//log("calling callback:" + ii); | |
callbacks[ii](event); | |
} | |
if (restoreTimeout_ >= 0) { | |
setTimeout(function() { | |
canvas.restoreContext(); | |
}, restoreTimeout_); | |
} | |
}, 0); | |
} | |
}; | |
canvas.restoreContext = function() { | |
if (contextLost_) { | |
if (onRestored_.length) { | |
setTimeout(function() { | |
if (!canRestore_) { | |
throw "can not restore. webglcontestlost listener did not call event.preventDefault"; | |
} | |
freeResources(); | |
resetToInitialState(unwrappedContext_); | |
contextLost_ = false; | |
numCalls_ = 0; | |
canRestore_ = false; | |
var callbacks = onRestored_.slice(); | |
var event = makeWebGLContextEvent("context restored"); | |
for (var ii = 0; ii < callbacks.length; ++ii) { | |
callbacks[ii](event); | |
} | |
}, 0); | |
} | |
} | |
}; | |
canvas.loseContextInNCalls = function(numCalls) { | |
if (contextLost_) { | |
throw "You can not ask a lost contet to be lost"; | |
} | |
numCallsToLoseContext_ = numCalls_ + numCalls; | |
}; | |
canvas.getNumCalls = function() { | |
return numCalls_; | |
}; | |
canvas.setRestoreTimeout = function(timeout) { | |
restoreTimeout_ = timeout; | |
}; | |
function isWebGLObject(obj) { | |
//return false; | |
return (obj instanceof WebGLBuffer || | |
obj instanceof WebGLFramebuffer || | |
obj instanceof WebGLProgram || | |
obj instanceof WebGLRenderbuffer || | |
obj instanceof WebGLShader || | |
obj instanceof WebGLTexture); | |
} | |
function checkResources(args) { | |
for (var ii = 0; ii < args.length; ++ii) { | |
var arg = args[ii]; | |
if (isWebGLObject(arg)) { | |
return arg.__webglDebugContextLostId__ == contextId_; | |
} | |
} | |
return true; | |
} | |
function clearErrors() { | |
var k = Object.keys(glErrorShadow_); | |
for (var ii = 0; ii < k.length; ++ii) { | |
delete glErrorShadow_[k]; | |
} | |
} | |
function loseContextIfTime() { | |
++numCalls_; | |
if (!contextLost_) { | |
if (numCallsToLoseContext_ == numCalls_) { | |
canvas.loseContext(); | |
} | |
} | |
} | |
// Makes a function that simulates WebGL when out of context. | |
function makeLostContextFunctionWrapper(ctx, functionName) { | |
var f = ctx[functionName]; | |
return function() { | |
// log("calling:" + functionName); | |
// Only call the functions if the context is not lost. | |
loseContextIfTime(); | |
if (!contextLost_) { | |
//if (!checkResources(arguments)) { | |
// glErrorShadow_[wrappedContext_.INVALID_OPERATION] = true; | |
// return; | |
//} | |
var result = f.apply(ctx, arguments); | |
return result; | |
} | |
}; | |
} | |
function freeResources() { | |
for (var ii = 0; ii < resourceDb_.length; ++ii) { | |
var resource = resourceDb_[ii]; | |
if (resource instanceof WebGLBuffer) { | |
unwrappedContext_.deleteBuffer(resource); | |
} else if (resource instanceof WebGLFramebuffer) { | |
unwrappedContext_.deleteFramebuffer(resource); | |
} else if (resource instanceof WebGLProgram) { | |
unwrappedContext_.deleteProgram(resource); | |
} else if (resource instanceof WebGLRenderbuffer) { | |
unwrappedContext_.deleteRenderbuffer(resource); | |
} else if (resource instanceof WebGLShader) { | |
unwrappedContext_.deleteShader(resource); | |
} else if (resource instanceof WebGLTexture) { | |
unwrappedContext_.deleteTexture(resource); | |
} | |
} | |
} | |
function makeWebGLContextEvent(statusMessage) { | |
return { | |
statusMessage: statusMessage, | |
preventDefault: function() { | |
canRestore_ = true; | |
} | |
}; | |
} | |
return canvas; | |
function makeLostContextSimulatingContext(ctx) { | |
// copy all functions and properties to wrapper | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'function') { | |
wrappedContext_[propertyName] = makeLostContextFunctionWrapper( | |
ctx, propertyName); | |
} else { | |
makePropertyWrapper(wrappedContext_, ctx, propertyName); | |
} | |
} | |
// Wrap a few functions specially. | |
wrappedContext_.getError = function() { | |
loseContextIfTime(); | |
if (!contextLost_) { | |
var err; | |
while (err = unwrappedContext_.getError()) { | |
glErrorShadow_[err] = true; | |
} | |
} | |
for (var err in glErrorShadow_) { | |
if (glErrorShadow_[err]) { | |
delete glErrorShadow_[err]; | |
return err; | |
} | |
} | |
return wrappedContext_.NO_ERROR; | |
}; | |
var creationFunctions = [ | |
"createBuffer", | |
"createFramebuffer", | |
"createProgram", | |
"createRenderbuffer", | |
"createShader", | |
"createTexture" | |
]; | |
for (var ii = 0; ii < creationFunctions.length; ++ii) { | |
var functionName = creationFunctions[ii]; | |
wrappedContext_[functionName] = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return null; | |
} | |
var obj = f.apply(ctx, arguments); | |
obj.__webglDebugContextLostId__ = contextId_; | |
resourceDb_.push(obj); | |
return obj; | |
}; | |
}(ctx[functionName]); | |
} | |
var functionsThatShouldReturnNull = [ | |
"getActiveAttrib", | |
"getActiveUniform", | |
"getBufferParameter", | |
"getContextAttributes", | |
"getAttachedShaders", | |
"getFramebufferAttachmentParameter", | |
"getParameter", | |
"getProgramParameter", | |
"getProgramInfoLog", | |
"getRenderbufferParameter", | |
"getShaderParameter", | |
"getShaderInfoLog", | |
"getShaderSource", | |
"getTexParameter", | |
"getUniform", | |
"getUniformLocation", | |
"getVertexAttrib" | |
]; | |
for (var ii = 0; ii < functionsThatShouldReturnNull.length; ++ii) { | |
var functionName = functionsThatShouldReturnNull[ii]; | |
wrappedContext_[functionName] = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return null; | |
} | |
return f.apply(ctx, arguments); | |
} | |
}(wrappedContext_[functionName]); | |
} | |
var isFunctions = [ | |
"isBuffer", | |
"isEnabled", | |
"isFramebuffer", | |
"isProgram", | |
"isRenderbuffer", | |
"isShader", | |
"isTexture" | |
]; | |
for (var ii = 0; ii < isFunctions.length; ++ii) { | |
var functionName = isFunctions[ii]; | |
wrappedContext_[functionName] = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return false; | |
} | |
return f.apply(ctx, arguments); | |
} | |
}(wrappedContext_[functionName]); | |
} | |
wrappedContext_.checkFramebufferStatus = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return wrappedContext_.FRAMEBUFFER_UNSUPPORTED; | |
} | |
return f.apply(ctx, arguments); | |
}; | |
}(wrappedContext_.checkFramebufferStatus); | |
wrappedContext_.getAttribLocation = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return -1; | |
} | |
return f.apply(ctx, arguments); | |
}; | |
}(wrappedContext_.getAttribLocation); | |
wrappedContext_.getVertexAttribOffset = function(f) { | |
return function() { | |
loseContextIfTime(); | |
if (contextLost_) { | |
return 0; | |
} | |
return f.apply(ctx, arguments); | |
}; | |
}(wrappedContext_.getVertexAttribOffset); | |
wrappedContext_.isContextLost = function() { | |
return contextLost_; | |
}; | |
return wrappedContext_; | |
} | |
} | |
return { | |
/** | |
* Initializes this module. Safe to call more than once. | |
* @param {!WebGLRenderingContext} ctx A WebGL context. If | |
} | |
* you have more than one context it doesn't matter which one | |
* you pass in, it is only used to pull out constants. | |
*/ | |
'init': init, | |
/** | |
* Returns true or false if value matches any WebGL enum | |
* @param {*} value Value to check if it might be an enum. | |
* @return {boolean} True if value matches one of the WebGL defined enums | |
*/ | |
'mightBeEnum': mightBeEnum, | |
/** | |
* Gets an string version of an WebGL enum. | |
* | |
* Example: | |
* WebGLDebugUtil.init(ctx); | |
* var str = WebGLDebugUtil.glEnumToString(ctx.getError()); | |
* | |
* @param {number} value Value to return an enum for | |
* @return {string} The string version of the enum. | |
*/ | |
'glEnumToString': glEnumToString, | |
/** | |
* Converts the argument of a WebGL function to a string. | |
* Attempts to convert enum arguments to strings. | |
* | |
* Example: | |
* WebGLDebugUtil.init(ctx); | |
* var str = WebGLDebugUtil.glFunctionArgToString('bindTexture', 0, gl.TEXTURE_2D); | |
* | |
* would return 'TEXTURE_2D' | |
* | |
* @param {string} functionName the name of the WebGL function. | |
* @param {number} argumentIndx the index of the argument. | |
* @param {*} value The value of the argument. | |
* @return {string} The value as a string. | |
*/ | |
'glFunctionArgToString': glFunctionArgToString, | |
/** | |
* Converts the arguments of a WebGL function to a string. | |
* Attempts to convert enum arguments to strings. | |
* | |
* @param {string} functionName the name of the WebGL function. | |
* @param {number} args The arguments. | |
* @return {string} The arguments as a string. | |
*/ | |
'glFunctionArgsToString': glFunctionArgsToString, | |
/** | |
* Given a WebGL context returns a wrapped context that calls | |
* gl.getError after every command and calls a function if the | |
* result is not NO_ERROR. | |
* | |
* You can supply your own function if you want. For example, if you'd like | |
* an exception thrown on any GL error you could do this | |
* | |
* function throwOnGLError(err, funcName, args) { | |
* throw WebGLDebugUtils.glEnumToString(err) + | |
* " was caused by call to " + funcName; | |
* }; | |
* | |
* ctx = WebGLDebugUtils.makeDebugContext( | |
* canvas.getContext("webgl"), throwOnGLError); | |
* | |
* @param {!WebGLRenderingContext} ctx The webgl context to wrap. | |
* @param {!function(err, funcName, args): void} opt_onErrorFunc The function | |
* to call when gl.getError returns an error. If not specified the default | |
* function calls console.log with a message. | |
* @param {!function(funcName, args): void} opt_onFunc The | |
* function to call when each webgl function is called. You | |
* can use this to log all calls for example. | |
*/ | |
'makeDebugContext': makeDebugContext, | |
/** | |
* Given a canvas element returns a wrapped canvas element that will | |
* simulate lost context. The canvas returned adds the following functions. | |
* | |
* loseContext: | |
* simulates a lost context event. | |
* | |
* restoreContext: | |
* simulates the context being restored. | |
* | |
* lostContextInNCalls: | |
* loses the context after N gl calls. | |
* | |
* getNumCalls: | |
* tells you how many gl calls there have been so far. | |
* | |
* setRestoreTimeout: | |
* sets the number of milliseconds until the context is restored | |
* after it has been lost. Defaults to 0. Pass -1 to prevent | |
* automatic restoring. | |
* | |
* @param {!Canvas} canvas The canvas element to wrap. | |
*/ | |
'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas, | |
/** | |
* Resets a context to the initial state. | |
* @param {!WebGLRenderingContext} ctx The webgl context to | |
* reset. | |
*/ | |
'resetToInitialState': resetToInitialState | |
}; | |
}(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Licensed under a BSD license. See ../license.html for license | |
// These funcitions are meant solely to help unclutter the tutorials. | |
// They are not meant as production type functions. | |
(function() { | |
/** | |
* Wrapped logging function. | |
* @param {string} msg The message to log. | |
*/ | |
var log = function(msg) { | |
if (window.console && window.console.log) { | |
window.console.log(msg); | |
} | |
}; | |
/** | |
* Wrapped logging function. | |
* @param {string} msg The message to log. | |
*/ | |
var error = function(msg) { | |
if (window.console) { | |
if (window.console.error) { | |
window.console.error(msg); | |
} | |
else if (window.console.log) { | |
window.console.log(msg); | |
} | |
} | |
}; | |
/** | |
* Turn off all logging. | |
*/ | |
var loggingOff = function() { | |
log = function() {}; | |
error = function() {}; | |
}; | |
/** | |
* Check if the page is embedded. | |
* @return {boolean} True of we are in an iframe | |
*/ | |
var isInIFrame = function() { | |
return window != window.top; | |
}; | |
/** | |
* Converts a WebGL enum to a string | |
* @param {!WebGLContext} gl The WebGLContext to use. | |
* @param {number} value The enum value. | |
* @return {string} The enum as a string. | |
*/ | |
var glEnumToString = function(gl, value) { | |
for (var p in gl) { | |
if (gl[p] == value) { | |
return p; | |
} | |
} | |
return "0x" + value.toString(16); | |
}; | |
/** | |
* Creates the HTLM for a failure message | |
* @param {string} canvasContainerId id of container of th | |
* canvas. | |
* @return {string} The html. | |
*/ | |
var makeFailHTML = function(msg) { | |
return '' + | |
'<table style="background-color: #8CE; width: 100%; height: 100%;"><tr>' + | |
'<td align="center">' + | |
'<div style="display: table-cell; vertical-align: middle;">' + | |
'<div style="">' + msg + '</div>' + | |
'</div>' + | |
'</td></tr></table>'; | |
}; | |
/** | |
* Mesasge for getting a webgl browser | |
* @type {string} | |
*/ | |
var GET_A_WEBGL_BROWSER = '' + | |
'This page requires a browser that supports WebGL.<br/>' + | |
'<a href="http://get.webgl.org">Click here to upgrade your browser.</a>'; | |
/** | |
* Mesasge for need better hardware | |
* @type {string} | |
*/ | |
var OTHER_PROBLEM = '' + | |
"It doesn't appear your computer can support WebGL.<br/>" + | |
'<a href="http://get.webgl.org/troubleshooting/">Click here for more information.</a>'; | |
/** | |
* Creates a webgl context. If creation fails it will | |
* change the contents of the container of the <canvas> | |
* tag to an error message with the correct links for WebGL. | |
* @param {Element} canvas. The canvas element to create a | |
* context from. | |
* @param {WebGLContextCreationAttirbutes} opt_attribs Any | |
* creation attributes you want to pass in. | |
* @return {WebGLRenderingContext} The created context. | |
*/ | |
var setupWebGL = function(canvas, opt_attribs) { | |
function showLink(str) { | |
var container = canvas.parentNode; | |
if (container) { | |
container.innerHTML = makeFailHTML(str); | |
} | |
}; | |
if (!window.WebGLRenderingContext) { | |
showLink(GET_A_WEBGL_BROWSER); | |
return null; | |
} | |
var context = create3DContext(canvas, opt_attribs); | |
if (!context) { | |
showLink(OTHER_PROBLEM); | |
} | |
return context; | |
}; | |
/** | |
* Creates a webgl context. | |
* @param {!Canvas} canvas The canvas tag to get context | |
* from. If one is not passed in one will be created. | |
* @return {!WebGLContext} The created context. | |
*/ | |
var create3DContext = function(canvas, opt_attribs) { | |
var names = ["webgl", "experimental-webgl"]; | |
var context = null; | |
for (var ii = 0; ii < names.length; ++ii) { | |
try { | |
context = canvas.getContext(names[ii], opt_attribs); | |
} catch(e) {} | |
if (context) { | |
break; | |
} | |
} | |
return context; | |
} | |
var updateCSSIfInIFrame = function() { | |
if (isInIFrame()) { | |
document.body.className = "iframe"; | |
} | |
}; | |
/** | |
* Gets a WebGL context. | |
* makes its backing store the size it is displayed. | |
*/ | |
var getWebGLContext = function(canvas, opt_attribs) { | |
if (isInIFrame()) { | |
updateCSSIfInIFrame(); | |
// make the canvas backing store the size it's displayed. | |
canvas.width = canvas.clientWidth; | |
canvas.height = canvas.clientHeight; | |
} else { | |
var title = document.title; | |
var h1 = document.createElement("h1"); | |
h1.innerText = title; | |
document.body.insertBefore(h1, document.body.children[0]); | |
} | |
var gl = setupWebGL(canvas, opt_attribs); | |
return gl; | |
}; | |
/** | |
* Loads a shader. | |
* @param {!WebGLContext} gl The WebGLContext to use. | |
* @param {string} shaderSource The shader source. | |
* @param {number} shaderType The type of shader. | |
* @param {function(string): void) opt_errorCallback callback for errors. | |
* @return {!WebGLShader} The created shader. | |
*/ | |
var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) { | |
var errFn = opt_errorCallback || error; | |
// Create the shader object | |
var shader = gl.createShader(shaderType); | |
// Load the shader source | |
gl.shaderSource(shader, shaderSource); | |
// Compile the shader | |
gl.compileShader(shader); | |
// Check the compile status | |
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); | |
if (!compiled) { | |
// Something went wrong during compilation; get the error | |
lastError = gl.getShaderInfoLog(shader); | |
errFn("*** Error compiling shader '" + shader + "':" + lastError); | |
gl.deleteShader(shader); | |
return null; | |
} | |
return shader; | |
} | |
/** | |
* Creates a program, attaches shaders, binds attrib locations, links the | |
* program and calls useProgram. | |
* @param {!Array.<!WebGLShader>} shaders The shaders to attach | |
* @param {!Array.<string>} opt_attribs The attribs names. | |
* @param {!Array.<number>} opt_locations The locations for the attribs. | |
* @param {function(string): void) opt_errorCallback callback for errors. | |
*/ | |
var loadProgram = function( | |
gl, shaders, opt_attribs, opt_locations, opt_errorCallback) { | |
var errFn = opt_errorCallback || error; | |
var program = gl.createProgram(); | |
for (var ii = 0; ii < shaders.length; ++ii) { | |
gl.attachShader(program, shaders[ii]); | |
} | |
if (opt_attribs) { | |
for (var ii = 0; ii < opt_attribs.length; ++ii) { | |
gl.bindAttribLocation( | |
program, | |
opt_locations ? opt_locations[ii] : ii, | |
opt_attribs[ii]); | |
} | |
} | |
gl.linkProgram(program); | |
// Check the link status | |
var linked = gl.getProgramParameter(program, gl.LINK_STATUS); | |
if (!linked) { | |
// something went wrong with the link | |
lastError = gl.getProgramInfoLog (program); | |
errFn("Error in program linking:" + lastError); | |
gl.deleteProgram(program); | |
return null; | |
} | |
return program; | |
}; | |
/** | |
* Loads a shader from a script tag. | |
* @param {!WebGLContext} gl The WebGLContext to use. | |
* @param {string} scriptId The id of the script tag. | |
* @param {number} opt_shaderType The type of shader. If not passed in it will | |
* be derived from the type of the script tag. | |
* @param {function(string): void) opt_errorCallback callback for errors. | |
* @return {!WebGLShader} The created shader. | |
*/ | |
var createShaderFromScript = function( | |
gl, scriptId, opt_shaderType, opt_errorCallback) { | |
var shaderSource = ""; | |
var shaderType; | |
var shaderScript = document.getElementById(scriptId); | |
if (!shaderScript) { | |
throw("*** Error: unknown script element" + scriptId); | |
} | |
shaderSource = shaderScript.text; | |
if (!opt_shaderType) { | |
if (shaderScript.type == "x-shader/x-vertex") { | |
shaderType = gl.VERTEX_SHADER; | |
} else if (shaderScript.type == "x-shader/x-fragment") { | |
shaderType = gl.FRAGMENT_SHADER; | |
} else if (shaderType != gl.VERTEX_SHADER && shaderType != gl.FRAGMENT_SHADER) { | |
throw("*** Error: unknown shader type"); | |
return null; | |
} | |
} | |
return loadShader( | |
gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType, | |
opt_errorCallback); | |
}; | |
var defaultShaderType = [ | |
"VERTEX_SHADER", | |
"FRAGMENT_SHADER" | |
]; | |
/** | |
* Creates a program from 2 script tags. | |
* | |
* @param {!WebGLContext} gl The WebGLContext to use. | |
* @param {!Array.<string>} shaderScriptIds Array of ids of the | |
* script tags for the shaders. The first is assumed to | |
* be the vertex shader, the second the fragment shader. | |
* @param {!Array.<string>} opt_attribs The attribs names. | |
* @param {!Array.<number>} opt_locations The locations for the attribs. | |
* @param {function(string): void) opt_errorCallback callback for errors. | |
* @return {!WebGLProgram} The created program. | |
*/ | |
var createProgramFromScripts = function( | |
gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) { | |
var shaders = []; | |
for (var ii = 0; ii < shaderScriptIds.length; ++ii) { | |
shaders.push(createShaderFromScript( | |
gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback)); | |
} | |
return loadProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback); | |
}; | |
// Add your prefix here. | |
var browserPrefixes = [ | |
"", | |
"MOZ_", | |
"OP_", | |
"WEBKIT_" | |
]; | |
/** | |
* Given an extension name like WEBGL_compressed_texture_s3tc | |
* returns the supported version extension, like | |
* WEBKIT_WEBGL_compressed_teture_s3tc | |
* @param {string} name Name of extension to look for | |
* @return {WebGLExtension} The extension or undefined if not | |
* found. | |
*/ | |
var getExtensionWithKnownPrefixes = function(gl, name) { | |
for (var ii = 0; ii < browserPrefixes.length; ++ii) { | |
var prefixedName = browserPrefixes[ii] + name; | |
var ext = gl.getExtension(prefixedName); | |
if (ext) { | |
return ext; | |
} | |
} | |
}; | |
/** | |
* Resize a canvas to match the size it's displayed. | |
* @param {!Canvas} canvas The canvas to resize. | |
*/ | |
var resizeCanvasToDisplaySize = function(canvas) { | |
if (canvas.width != canvas.clientWidth || | |
canvas.height != canvas.clientHeight) { | |
canvas.width = canvas.clientWidth; | |
canvas.height = canvas.clientHeight; | |
} | |
} | |
/* export functions */ | |
this.createProgram = loadProgram; | |
this.createProgramFromScripts = createProgramFromScripts; | |
this.createShaderFromScriptElement = createShaderFromScript; | |
this.getWebGLContext = getWebGLContext; | |
this.updateCSSIfInIFrame = updateCSSIfInIFrame; | |
this.getExtensionWithKnownPrefixes = getExtensionWithKnownPrefixes; | |
this.resizeCanvasToDisplaySize = resizeCanvasToDisplaySize; | |
/** | |
* Provides requestAnimationFrame in a cross browser way. | |
*/ | |
this.requestAnimFrame = (function() { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { | |
return window.setTimeout(callback, 1000/60); | |
}; | |
})(); | |
/** | |
* Provides cancelRequestAnimationFrame in a cross browser way. | |
*/ | |
this.cancelRequestAnimFrame = (function() { | |
return window.cancelCancelRequestAnimationFrame || | |
window.webkitCancelRequestAnimationFrame || | |
window.mozCancelRequestAnimationFrame || | |
window.oCancelRequestAnimationFrame || | |
window.msCancelRequestAnimationFrame || | |
window.clearTimeout; | |
})(); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment