Last active
December 19, 2015 00:28
-
-
Save adrianseeley/5868524 to your computer and use it in GitHub Desktop.
GPU encoding floats on gpu, and reading those floats works, textures dont seem to be binding though...
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> | |
<title>GPU</title> | |
<script> | |
function log (msg) { if (window.console && window.console.log) { console.log(msg); } } | |
function handleContextLost (e) { log("handle context lost"); e.preventDefault(); } | |
function handleContextRestored () { log("handle context restored"); init(); } | |
function checkGLError () { var error = GPU_Static.gl_context.getError(); if (error != GPU_Static.gl_context.NO_ERROR && error != GPU_Static.gl_context.CONTEXT_LOST_WEBGL) { var str = "GL Error: " + error; document.body.appendChild(document.createTextNode(str)); throw str; } } | |
WebGLUtils = function () { | |
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>'; | |
}; | |
var GET_A_WEBGL_BROWSER = 'This page requires a browser that supports WebGL.<br/><a href="http://get.webgl.org">lick here to upgrade your browser.</a>'; | |
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>'; | |
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; | |
}; | |
var create3DContext = function(canvas, opt_attribs) { | |
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-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; | |
} | |
return {create3DContext: create3DContext, setupWebGL: setupWebGL}; | |
}(); | |
WebGLDebugUtils = function() { | |
var log = function(msg) { | |
if (window.console && window.console.log) { | |
window.console.log(msg); | |
} | |
}; | |
var error = function(msg) { | |
if (window.console && window.console.error) { | |
window.console.error(msg); | |
} else { | |
log(msg); | |
} | |
}; | |
var glValidEnumContexts = {'enable': {1: { 0:true }}, 'disable': {1: {0: true}}, 'getParameter': {1: {0: true}}, 'drawArrays': {3: {0: true}}, 'drawElements': {4: {0: true, 2: true}}, 'createShader': {1: {0: true}}, 'getShaderParameter': {2: {1: true}}, 'getProgramParameter': {2: {1: true}}, 'getShaderPrecisionFormat': {2: {0: true, 1: true}}, 'getVertexAttrib': {2: {1: true}}, 'vertexAttribPointer': {6: {2: true}}, 'bindTexture': {2: {0: true}}, 'activeTexture': {1: {0: true}}, 'getTexParameter': {2: {0: true, 1: true}}, 'texParameterf': {3: {0:true, 1: true}}, 'texParameteri': {3: {0: true, 1: true, 2: true}}, 'texImage2D': {9: {0: true, 2: true, 6: true, 7: true}, 6: {0: true, 2: true, 3: true, 4: true}}, 'texSubImage2D': {9: {0: true, 6: true, 7: true}, 7: {0: true, 4: true, 5: true}}, 'copyTexImage2D': {8: {0: true, 2: true}}, 'copyTexSubImage2D': {8: {0: true}}, 'generateMipmap': {1: {0: true}}, 'compressedTexImage2D': {7: {0: true, 2: true}}, 'compressedTexSubImage2D': {8: {0: true, 6: true}},'bindBuffer': {2: {0: true}},'bufferData': {3: {0: true, 2: true}},'bufferSubData': {3: {0: true}},'getBufferParameter': {2: {0: true, 1: true}}, 'pixelStorei': {2: {0: true, 1: true}}, 'readPixels': {7: {4: true, 5: true}}, 'bindRenderbuffer': {2: {0: true}}, 'bindFramebuffer': {2: {0: true}}, 'checkFramebufferStatus': {1: {0: true}}, 'framebufferRenderbuffer': {4: {0: true, 1: true, 2: true}}, 'framebufferTexture2D': {5: {0: true, 1: true, 2: true}}, 'getFramebufferAttachmentParameter': {3: {0: true, 1: true, 2: true}}, 'getRenderbufferParameter': {2: {0: true, 1: true}}, 'renderbufferStorage': {4: {0: true, 1: true}}, 'clear': {1: {0: true}}, 'depthFunc': {1: {0: true}}, 'blendFunc': {2: {0: true, 1: true}}, 'blendFuncSeparate': {4: { 0: true, 1: true, 2: true, 3: true}}, 'blendEquation': {1: {0: true}}, 'blendEquationSeparate': {2: {0: true, 1: true}}, 'stencilFunc': {3: {0: true}}, 'stencilFuncSeparate': {4: {0: true, 1: true}}, 'stencilMaskSeparate': {2: { 0: true}}, 'stencilOp': {3: {0: true, 1: true, 2: true}}, 'stencilOpSeparate': {4: {0: true, 1: true, 2: true, 3: true}}, 'cullFace': {1: {0: true}}, 'frontFace': {1: {0: true}}}; | |
var glEnums = null; | |
function init (ctx) { | |
if (glEnums == null) { | |
glEnums = {}; | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'number') { | |
glEnums[ctx[propertyName]] = propertyName; | |
} | |
} | |
} | |
} | |
function checkInit () { | |
if (glEnums == null) { | |
throw 'WebGLDebugUtils.init(ctx) not called'; | |
} | |
} | |
function mightBeEnum (value) { | |
checkInit(); | |
return (glEnums[value] !== undefined); | |
} | |
function glEnumToString (value) { | |
checkInit(); | |
var name = glEnums[value]; | |
return (name !== undefined) ? ("gl." + name) : ("/*UNKNOWN WebGL ENUM*/ 0x" + value.toString(16) + ""); | |
} | |
function glFunctionArgToString (functionName, numArgs, argumentIndex, value) { | |
var funcInfo = glValidEnumContexts[functionName]; | |
if (funcInfo !== undefined) { | |
var funcInfo = funcInfo[numArgs]; | |
if (funcInfo !== undefined) { | |
if (funcInfo[argumentIndex]) { | |
return glEnumToString(value); | |
} | |
} | |
} | |
if (value === null) { | |
return "null"; | |
} else if (value === undefined) { | |
return "undefined"; | |
} else { | |
return value.toString(); | |
} | |
} | |
function glFunctionArgsToString (functionName, args) { | |
// apparently we can't do args.join(","); | |
var argStr = ""; | |
var numArgs = args.length; | |
for (var ii = 0; ii < numArgs; ++ii) { | |
argStr += ((ii == 0) ? '' : ', ') + glFunctionArgToString(functionName, numArgs, ii, args[ii]); | |
} | |
return argStr; | |
}; | |
function makePropertyWrapper (wrapper, original, propertyName) { | |
wrapper.__defineGetter__(propertyName, function() { | |
return original[propertyName]; | |
}); | |
wrapper.__defineSetter__(propertyName, function(value) { | |
original[propertyName] = value; | |
}); | |
} | |
function makeFunctionWrapper (original, functionName) { | |
var f = original[functionName]; | |
return function() { | |
var result = f.apply(original, arguments); | |
return result; | |
}; | |
} | |
function makeDebugContext (ctx, opt_onErrorFunc, opt_onFunc) { | |
init(ctx); | |
opt_onErrorFunc = opt_onErrorFunc || function(err, functionName, args) { | |
var argStr = ""; | |
var numArgs = args.length; | |
for (var ii = 0; ii < numArgs; ++ii) { | |
argStr += ((ii == 0) ? '' : ', ') + | |
glFunctionArgToString(functionName, numArgs, ii, args[ii]); | |
} | |
error("WebGL error "+ glEnumToString(err) + " in "+ functionName + "(" + argStr + ")"); | |
}; | |
var glErrorShadow = {}; | |
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; | |
}; | |
} | |
var wrapper = {}; | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'function') { | |
wrapper[propertyName] = makeErrorWrapper(ctx, propertyName); | |
} else { | |
makePropertyWrapper(wrapper, ctx, propertyName); | |
} | |
} | |
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); | |
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); | |
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; | |
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); | |
break; | |
} | |
}; | |
} | |
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() { | |
for (var ii = 0; ii < callbacks.length; ++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 (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(); | |
} | |
} | |
} | |
function makeLostContextFunctionWrapper(ctx, functionName) { | |
var f = ctx[functionName]; | |
return function() { | |
loseContextIfTime(); | |
if (!contextLost_) { | |
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) { | |
for (var propertyName in ctx) { | |
if (typeof ctx[propertyName] == 'function') { | |
wrappedContext_[propertyName] = makeLostContextFunctionWrapper(ctx, propertyName); | |
} else { | |
makePropertyWrapper(wrappedContext_, ctx, propertyName); | |
} | |
} | |
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 { | |
'init': init, | |
'mightBeEnum': mightBeEnum, | |
'glEnumToString': glEnumToString, | |
'glFunctionArgsToString': glFunctionArgsToString, | |
'makeDebugContext': makeDebugContext, | |
'makeLostContextSimulatingCanvas': makeLostContextSimulatingCanvas, | |
'resetToInitialState': resetToInitialState | |
}; | |
}(); | |
window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback, element) { return window.setTimeout(callback, 1000 / 60); }; })(); | |
window.cancelAnimFrame = (function() { return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || window.clearTimeout; })(); | |
function Matrix4x4() { | |
this.elements = Array(16); | |
this.loadIdentity(); | |
} | |
Matrix4x4.prototype = { | |
scale: function (sx, sy, sz) { | |
this.elements[0*4+0] *= sx; | |
this.elements[0*4+1] *= sx; | |
this.elements[0*4+2] *= sx; | |
this.elements[0*4+3] *= sx; | |
this.elements[1*4+0] *= sy; | |
this.elements[1*4+1] *= sy; | |
this.elements[1*4+2] *= sy; | |
this.elements[1*4+3] *= sy; | |
this.elements[2*4+0] *= sz; | |
this.elements[2*4+1] *= sz; | |
this.elements[2*4+2] *= sz; | |
this.elements[2*4+3] *= sz; | |
return this; | |
}, | |
translate: function (tx, ty, tz) { | |
this.elements[3*4+0] += this.elements[0*4+0] * tx + this.elements[1*4+0] * ty + this.elements[2*4+0] * tz; | |
this.elements[3*4+1] += this.elements[0*4+1] * tx + this.elements[1*4+1] * ty + this.elements[2*4+1] * tz; | |
this.elements[3*4+2] += this.elements[0*4+2] * tx + this.elements[1*4+2] * ty + this.elements[2*4+2] * tz; | |
this.elements[3*4+3] += this.elements[0*4+3] * tx + this.elements[1*4+3] * ty + this.elements[2*4+3] * tz; | |
return this; | |
}, | |
rotate: function (angle, x, y, z) { | |
var mag = Math.sqrt(x*x + y*y + z*z); | |
var sinAngle = Math.sin(angle * Math.PI / 180.0); | |
var cosAngle = Math.cos(angle * Math.PI / 180.0); | |
if (mag > 0) { | |
var xx, yy, zz, xy, yz, zx, xs, ys, zs; | |
var oneMinusCos; | |
var rotMat; | |
x /= mag; | |
y /= mag; | |
z /= mag; | |
xx = x * x; | |
yy = y * y; | |
zz = z * z; | |
xy = x * y; | |
yz = y * z; | |
zx = z * x; | |
xs = x * sinAngle; | |
ys = y * sinAngle; | |
zs = z * sinAngle; | |
oneMinusCos = 1.0 - cosAngle; | |
rotMat = new Matrix4x4(); | |
rotMat.elements[0*4+0] = (oneMinusCos * xx) + cosAngle; | |
rotMat.elements[0*4+1] = (oneMinusCos * xy) - zs; | |
rotMat.elements[0*4+2] = (oneMinusCos * zx) + ys; | |
rotMat.elements[0*4+3] = 0.0; | |
rotMat.elements[1*4+0] = (oneMinusCos * xy) + zs; | |
rotMat.elements[1*4+1] = (oneMinusCos * yy) + cosAngle; | |
rotMat.elements[1*4+2] = (oneMinusCos * yz) - xs; | |
rotMat.elements[1*4+3] = 0.0; | |
rotMat.elements[2*4+0] = (oneMinusCos * zx) - ys; | |
rotMat.elements[2*4+1] = (oneMinusCos * yz) + xs; | |
rotMat.elements[2*4+2] = (oneMinusCos * zz) + cosAngle; | |
rotMat.elements[2*4+3] = 0.0; | |
rotMat.elements[3*4+0] = 0.0; | |
rotMat.elements[3*4+1] = 0.0; | |
rotMat.elements[3*4+2] = 0.0; | |
rotMat.elements[3*4+3] = 1.0; | |
rotMat = rotMat.multiply(this); | |
this.elements = rotMat.elements; | |
} | |
return this; | |
}, | |
frustum: function (left, right, bottom, top, nearZ, farZ) { | |
var deltaX = right - left; | |
var deltaY = top - bottom; | |
var deltaZ = farZ - nearZ; | |
var frust; | |
if ( (nearZ <= 0.0) || (farZ <= 0.0) || | |
(deltaX <= 0.0) || (deltaY <= 0.0) || (deltaZ <= 0.0) ) | |
return this; | |
frust = new Matrix4x4(); | |
frust.elements[0*4+0] = 2.0 * nearZ / deltaX; | |
frust.elements[0*4+1] = frust.elements[0*4+2] = frust.elements[0*4+3] = 0.0; | |
frust.elements[1*4+1] = 2.0 * nearZ / deltaY; | |
frust.elements[1*4+0] = frust.elements[1*4+2] = frust.elements[1*4+3] = 0.0; | |
frust.elements[2*4+0] = (right + left) / deltaX; | |
frust.elements[2*4+1] = (top + bottom) / deltaY; | |
frust.elements[2*4+2] = -(nearZ + farZ) / deltaZ; | |
frust.elements[2*4+3] = -1.0; | |
frust.elements[3*4+2] = -2.0 * nearZ * farZ / deltaZ; | |
frust.elements[3*4+0] = frust.elements[3*4+1] = frust.elements[3*4+3] = 0.0; | |
frust = frust.multiply(this); | |
this.elements = frust.elements; | |
return this; | |
}, | |
perspective: function (fovy, aspect, nearZ, farZ) { | |
var frustumH = Math.tan(fovy / 360.0 * Math.PI) * nearZ; | |
var frustumW = frustumH * aspect; | |
return this.frustum(-frustumW, frustumW, -frustumH, frustumH, nearZ, farZ); | |
}, | |
ortho: function (left, right, bottom, top, nearZ, farZ) { | |
var deltaX = right - left; | |
var deltaY = top - bottom; | |
var deltaZ = farZ - nearZ; | |
var ortho = new Matrix4x4(); | |
if ( (deltaX == 0.0) || (deltaY == 0.0) || (deltaZ == 0.0) ) | |
return this; | |
ortho.elements[0*4+0] = 2.0 / deltaX; | |
ortho.elements[3*4+0] = -(right + left) / deltaX; | |
ortho.elements[1*4+1] = 2.0 / deltaY; | |
ortho.elements[3*4+1] = -(top + bottom) / deltaY; | |
ortho.elements[2*4+2] = -2.0 / deltaZ; | |
ortho.elements[3*4+2] = -(nearZ + farZ) / deltaZ; | |
ortho = ortho.multiply(this); | |
this.elements = ortho.elements; | |
return this; | |
}, | |
multiply: function (right) { | |
var tmp = new Matrix4x4(); | |
for (var i = 0; i < 4; i++) { | |
tmp.elements[i*4+0] = | |
(this.elements[i*4+0] * right.elements[0*4+0]) + | |
(this.elements[i*4+1] * right.elements[1*4+0]) + | |
(this.elements[i*4+2] * right.elements[2*4+0]) + | |
(this.elements[i*4+3] * right.elements[3*4+0]) ; | |
tmp.elements[i*4+1] = | |
(this.elements[i*4+0] * right.elements[0*4+1]) + | |
(this.elements[i*4+1] * right.elements[1*4+1]) + | |
(this.elements[i*4+2] * right.elements[2*4+1]) + | |
(this.elements[i*4+3] * right.elements[3*4+1]) ; | |
tmp.elements[i*4+2] = | |
(this.elements[i*4+0] * right.elements[0*4+2]) + | |
(this.elements[i*4+1] * right.elements[1*4+2]) + | |
(this.elements[i*4+2] * right.elements[2*4+2]) + | |
(this.elements[i*4+3] * right.elements[3*4+2]) ; | |
tmp.elements[i*4+3] = | |
(this.elements[i*4+0] * right.elements[0*4+3]) + | |
(this.elements[i*4+1] * right.elements[1*4+3]) + | |
(this.elements[i*4+2] * right.elements[2*4+3]) + | |
(this.elements[i*4+3] * right.elements[3*4+3]) ; | |
} | |
this.elements = tmp.elements; | |
return this; | |
}, | |
copy: function () { | |
var tmp = new Matrix4x4(); | |
for (var i = 0; i < 16; i++) { | |
tmp.elements[i] = this.elements[i]; | |
} | |
return tmp; | |
}, | |
get: function (row, col) { | |
return this.elements[4*row+col]; | |
}, | |
// In-place inversion | |
invert: function () { | |
var tmp_0 = this.get(2,2) * this.get(3,3); | |
var tmp_1 = this.get(3,2) * this.get(2,3); | |
var tmp_2 = this.get(1,2) * this.get(3,3); | |
var tmp_3 = this.get(3,2) * this.get(1,3); | |
var tmp_4 = this.get(1,2) * this.get(2,3); | |
var tmp_5 = this.get(2,2) * this.get(1,3); | |
var tmp_6 = this.get(0,2) * this.get(3,3); | |
var tmp_7 = this.get(3,2) * this.get(0,3); | |
var tmp_8 = this.get(0,2) * this.get(2,3); | |
var tmp_9 = this.get(2,2) * this.get(0,3); | |
var tmp_10 = this.get(0,2) * this.get(1,3); | |
var tmp_11 = this.get(1,2) * this.get(0,3); | |
var tmp_12 = this.get(2,0) * this.get(3,1); | |
var tmp_13 = this.get(3,0) * this.get(2,1); | |
var tmp_14 = this.get(1,0) * this.get(3,1); | |
var tmp_15 = this.get(3,0) * this.get(1,1); | |
var tmp_16 = this.get(1,0) * this.get(2,1); | |
var tmp_17 = this.get(2,0) * this.get(1,1); | |
var tmp_18 = this.get(0,0) * this.get(3,1); | |
var tmp_19 = this.get(3,0) * this.get(0,1); | |
var tmp_20 = this.get(0,0) * this.get(2,1); | |
var tmp_21 = this.get(2,0) * this.get(0,1); | |
var tmp_22 = this.get(0,0) * this.get(1,1); | |
var tmp_23 = this.get(1,0) * this.get(0,1); | |
var t0 = ((tmp_0 * this.get(1,1) + tmp_3 * this.get(2,1) + tmp_4 * this.get(3,1)) - | |
(tmp_1 * this.get(1,1) + tmp_2 * this.get(2,1) + tmp_5 * this.get(3,1))); | |
var t1 = ((tmp_1 * this.get(0,1) + tmp_6 * this.get(2,1) + tmp_9 * this.get(3,1)) - | |
(tmp_0 * this.get(0,1) + tmp_7 * this.get(2,1) + tmp_8 * this.get(3,1))); | |
var t2 = ((tmp_2 * this.get(0,1) + tmp_7 * this.get(1,1) + tmp_10 * this.get(3,1)) - | |
(tmp_3 * this.get(0,1) + tmp_6 * this.get(1,1) + tmp_11 * this.get(3,1))); | |
var t3 = ((tmp_5 * this.get(0,1) + tmp_8 * this.get(1,1) + tmp_11 * this.get(2,1)) - | |
(tmp_4 * this.get(0,1) + tmp_9 * this.get(1,1) + tmp_10 * this.get(2,1))); | |
var d = 1.0 / (this.get(0,0) * t0 + this.get(1,0) * t1 + this.get(2,0) * t2 + this.get(3,0) * t3); | |
var out_00 = d * t0; | |
var out_01 = d * t1; | |
var out_02 = d * t2; | |
var out_03 = d * t3; | |
var out_10 = d * ((tmp_1 * this.get(1,0) + tmp_2 * this.get(2,0) + tmp_5 * this.get(3,0)) - | |
(tmp_0 * this.get(1,0) + tmp_3 * this.get(2,0) + tmp_4 * this.get(3,0))); | |
var out_11 = d * ((tmp_0 * this.get(0,0) + tmp_7 * this.get(2,0) + tmp_8 * this.get(3,0)) - | |
(tmp_1 * this.get(0,0) + tmp_6 * this.get(2,0) + tmp_9 * this.get(3,0))); | |
var out_12 = d * ((tmp_3 * this.get(0,0) + tmp_6 * this.get(1,0) + tmp_11 * this.get(3,0)) - | |
(tmp_2 * this.get(0,0) + tmp_7 * this.get(1,0) + tmp_10 * this.get(3,0))); | |
var out_13 = d * ((tmp_4 * this.get(0,0) + tmp_9 * this.get(1,0) + tmp_10 * this.get(2,0)) - | |
(tmp_5 * this.get(0,0) + tmp_8 * this.get(1,0) + tmp_11 * this.get(2,0))); | |
var out_20 = d * ((tmp_12 * this.get(1,3) + tmp_15 * this.get(2,3) + tmp_16 * this.get(3,3)) - | |
(tmp_13 * this.get(1,3) + tmp_14 * this.get(2,3) + tmp_17 * this.get(3,3))); | |
var out_21 = d * ((tmp_13 * this.get(0,3) + tmp_18 * this.get(2,3) + tmp_21 * this.get(3,3)) - | |
(tmp_12 * this.get(0,3) + tmp_19 * this.get(2,3) + tmp_20 * this.get(3,3))); | |
var out_22 = d * ((tmp_14 * this.get(0,3) + tmp_19 * this.get(1,3) + tmp_22 * this.get(3,3)) - | |
(tmp_15 * this.get(0,3) + tmp_18 * this.get(1,3) + tmp_23 * this.get(3,3))); | |
var out_23 = d * ((tmp_17 * this.get(0,3) + tmp_20 * this.get(1,3) + tmp_23 * this.get(2,3)) - | |
(tmp_16 * this.get(0,3) + tmp_21 * this.get(1,3) + tmp_22 * this.get(2,3))); | |
var out_30 = d * ((tmp_14 * this.get(2,2) + tmp_17 * this.get(3,2) + tmp_13 * this.get(1,2)) - | |
(tmp_16 * this.get(3,2) + tmp_12 * this.get(1,2) + tmp_15 * this.get(2,2))); | |
var out_31 = d * ((tmp_20 * this.get(3,2) + tmp_12 * this.get(0,2) + tmp_19 * this.get(2,2)) - | |
(tmp_18 * this.get(2,2) + tmp_21 * this.get(3,2) + tmp_13 * this.get(0,2))); | |
var out_32 = d * ((tmp_18 * this.get(1,2) + tmp_23 * this.get(3,2) + tmp_15 * this.get(0,2)) - | |
(tmp_22 * this.get(3,2) + tmp_14 * this.get(0,2) + tmp_19 * this.get(1,2))); | |
var out_33 = d * ((tmp_22 * this.get(2,2) + tmp_16 * this.get(0,2) + tmp_21 * this.get(1,2)) - | |
(tmp_20 * this.get(1,2) + tmp_23 * this.get(2,2) + tmp_17 * this.get(0,2))); | |
this.elements[0*4+0] = out_00; | |
this.elements[0*4+1] = out_01; | |
this.elements[0*4+2] = out_02; | |
this.elements[0*4+3] = out_03; | |
this.elements[1*4+0] = out_10; | |
this.elements[1*4+1] = out_11; | |
this.elements[1*4+2] = out_12; | |
this.elements[1*4+3] = out_13; | |
this.elements[2*4+0] = out_20; | |
this.elements[2*4+1] = out_21; | |
this.elements[2*4+2] = out_22; | |
this.elements[2*4+3] = out_23; | |
this.elements[3*4+0] = out_30; | |
this.elements[3*4+1] = out_31; | |
this.elements[3*4+2] = out_32; | |
this.elements[3*4+3] = out_33; | |
return this; | |
}, | |
// Returns new matrix which is the inverse of this | |
inverse: function () { | |
var tmp = this.copy(); | |
return tmp.invert(); | |
}, | |
// In-place transpose | |
transpose: function () { | |
var tmp = this.elements[0*4+1]; | |
this.elements[0*4+1] = this.elements[1*4+0]; | |
this.elements[1*4+0] = tmp; | |
tmp = this.elements[0*4+2]; | |
this.elements[0*4+2] = this.elements[2*4+0]; | |
this.elements[2*4+0] = tmp; | |
tmp = this.elements[0*4+3]; | |
this.elements[0*4+3] = this.elements[3*4+0]; | |
this.elements[3*4+0] = tmp; | |
tmp = this.elements[1*4+2]; | |
this.elements[1*4+2] = this.elements[2*4+1]; | |
this.elements[2*4+1] = tmp; | |
tmp = this.elements[1*4+3]; | |
this.elements[1*4+3] = this.elements[3*4+1]; | |
this.elements[3*4+1] = tmp; | |
tmp = this.elements[2*4+3]; | |
this.elements[2*4+3] = this.elements[3*4+2]; | |
this.elements[3*4+2] = tmp; | |
return this; | |
}, | |
loadIdentity: function () { | |
for (var i = 0; i < 16; i++) | |
this.elements[i] = 0; | |
this.elements[0*4+0] = 1.0; | |
this.elements[1*4+1] = 1.0; | |
this.elements[2*4+2] = 1.0; | |
this.elements[3*4+3] = 1.0; | |
return this; | |
} | |
}; | |
</script> | |
<script> | |
var fragment_SHADER_STRING = [ | |
'precision mediump float;', | |
'uniform float time;', | |
'uniform sampler2D tex0;', | |
'uniform sampler2D tex1;', | |
'uniform sampler2D tex2;', | |
'uniform sampler2D tex3;', | |
'uniform sampler2D tex4;', | |
'uniform sampler2D tex5;', | |
'uniform sampler2D tex6;', | |
'uniform sampler2D tex7;', | |
'uniform sampler2D tex8;', | |
'uniform sampler2D tex9;', | |
'uniform sampler2D tex10;', | |
'uniform sampler2D tex11;', | |
'uniform sampler2D tex12;', | |
'uniform sampler2D tex13;', | |
'uniform sampler2D tex14;', | |
'uniform sampler2D tex15;', | |
'varying vec2 texCoord;', | |
'float random(vec2 seed) { return fract(cos(mod(123456780., 1024. * dot(seed / time, vec2(23.1406926327792690, 2.6651441426902251))))); }', | |
'float shift_right(float v, float amt) { v = floor(v) + 0.5; return floor(v / exp2(amt)); }', | |
'float shift_left(float v, float amt) { return floor(v * exp2(amt) + 0.5); }', | |
'float mask_last(float v, float bits) { return mod(v, shift_left(1.0, bits)); }', | |
'float extract_bits(float num, float from, float to) { from = floor(from + 0.5); to = floor(to + 0.5); return mask_last(shift_right(num, from), to - from); }', | |
'vec4 encode_float(float val) { if (val == 0.0) return vec4(0, 0, 0, 0); float sign = val > 0.0 ? 0.0 : 1.0; val = abs(val); float exponent = floor(log2(val)); float biased_exponent = exponent + 127.0; float fraction = ((val / exp2(exponent)) - 1.0) * 8388608.0; float t = biased_exponent / 2.0; float last_bit_of_biased_exponent = fract(t) * 2.0; float remaining_bits_of_biased_exponent = floor(t); float byte4 = extract_bits(fraction, 0.0, 8.0) / 255.0; float byte3 = extract_bits(fraction, 8.0, 16.0) / 255.0; float byte2 = (last_bit_of_biased_exponent * 128.0 + extract_bits(fraction, 16.0, 23.0)) / 255.0; float byte1 = (sign * 128.0 + remaining_bits_of_biased_exponent) / 255.0; return vec4(byte4, byte3, byte2, byte1); }', | |
'', | |
'{{user_functions}}', | |
'', | |
'void main() {', | |
'{{body}}', | |
//' vec4 data = texture2D(tex, texCoord);', | |
//' gl_FragColor = encode_float(random(texCoord));', | |
'}' | |
].join('\n'); | |
var VERTEX_SHADER_STRING = 'attribute vec3 g_Position; attribute vec2 g_TexCoord0; varying vec2 texCoord; void main() { gl_Position = vec4(g_Position.x, g_Position.y, g_Position.z, 1.0); texCoord = g_TexCoord0; }'; | |
function main () { | |
GPU_FloatsInFloatOut(document.getElementById('canvas'), { | |
user_functions: '', | |
body: ['vec4 data = texture2D(tex0, texCoord);', | |
'gl_FragColor = encode_float(30.);', | |
//'gl_FragColor.r = 1.;', | |
//'gl_FragColor.a = 1.;' | |
].join('\n') | |
}, 1, | |
function (fn) { | |
for (var i = 0; i < 20000; i++) | |
fn.set_input_value(i, 0, 5); | |
fn.prep_draw(); | |
fn.bind_textures_to_GPU(); | |
fn.draw(); | |
fn.read_floats_out(); | |
console.log(fn); | |
for (var i = 0; i < 10; i++) console.log(fn.output[i]); | |
}); | |
}; | |
var GPU_Static = {}; | |
function GPU_Initialize_Static (canvas) { | |
console.log('initialiing static gl instance with canvas', canvas); | |
// if a gl context hasnt already been created | |
if (!GPU_Static.hasOwnProperty('gl_context')) { | |
console.log('static instance did not already exist, creating'); | |
// hold the canvas | |
GPU_Static.canvas = canvas; | |
// initialize a WebGL context on the provided canvas | |
GPU_Static.gl_context = internal__initialize_gl(canvas); | |
// check for gl errors | |
checkGLError(); | |
// create the draw verticies | |
GPU_Static.verticies = new Float32Array([1, 1, 0, -1, 1, 0, -1, -1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0]); | |
// create the draw texture coordinates (UVs) | |
GPU_Static.texture_coordinates = new Float32Array([1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0]); | |
// create the draw texture coordinates (UVs) offset | |
GPU_Static.texture_coordinates_offset = GPU_Static.verticies.byteLength; | |
// load the vertex shader | |
GPU_Static.vertex_shader = internal__load_shader(GPU_Static.gl_context.VERTEX_SHADER, VERTEX_SHADER_STRING); | |
// check for gl errors | |
checkGLError(); | |
// get support for floating point texture components | |
// (RGBA values per pixel as floats instead of bytes) | |
GPU_Static.gl_context.getExtension('OES_texture_float'); | |
// check for gl errors | |
checkGLError(); | |
// create the vertex buffer object | |
GPU_Static.vertex_buffer_object = GPU_Static.gl_context.createBuffer(); | |
// check for gl errors | |
checkGLError(); | |
// bind the vertex buffer object | |
GPU_Static.gl_context.bindBuffer(GPU_Static.gl_context.ARRAY_BUFFER, GPU_Static.vertex_buffer_object); | |
// check for gl errors | |
checkGLError(); | |
// initilize the vertex buffer object | |
GPU_Static.gl_context.bufferData(GPU_Static.gl_context.ARRAY_BUFFER, GPU_Static.texture_coordinates_offset + GPU_Static.texture_coordinates.byteLength, GPU_Static.gl_context.STATIC_DRAW); | |
// check for gl errors | |
checkGLError(); | |
// fill vertex buffer object with our verticies | |
GPU_Static.gl_context.bufferSubData(GPU_Static.gl_context.ARRAY_BUFFER, 0, GPU_Static.verticies); | |
// check for gl errors | |
checkGLError(); | |
// fill vertex buffer object with our texture coordinates (UVs) | |
GPU_Static.gl_context.bufferSubData(GPU_Static.gl_context.ARRAY_BUFFER, GPU_Static.texture_coordinates_offset, GPU_Static.texture_coordinates); | |
// check for gl errors | |
checkGLError(); | |
} | |
console.log('static instance created'); | |
}; | |
function internal__initialize_gl (canvas) { | |
// attach handlers to loss/restoration of the gl context | |
canvas.addEventListener('webglcontextlost', handleContextLost, false); | |
canvas.addEventListener('webglcontextrestored', handleContextRestored, false); | |
// create the WebGL context | |
var gl_context = WebGLUtils.setupWebGL(canvas); | |
// if we were unable to create a context alert the user | |
if (!gl_context) throw 'ERROR: COULD NOT CREATE A GL CONTEXT'; | |
// set the clear color of the context | |
gl_context.clearColor(1, 0, 0, 0); | |
console.log('gl initialized'); | |
// return the created context | |
return gl_context; | |
}; | |
function GPU_BytesInBytesOut (canvas, fragment_shader_descriptor, in_parameter_count, callback) { | |
// wrapped function constructor | |
internal__GPU_constructor(canvas, fragment_shader_descriptor, in_parameter_count, true, true, callback); | |
}; | |
function GPU_BytesInFloatOut (canvas, fragment_shader_descriptor, in_parameter_count, callback) { | |
// wrapped function constructor | |
internal__GPU_constructor(canvas, fragment_shader_descriptor, in_parameter_count, true, false, callback); | |
}; | |
function GPU_FloatsInBytesOut (canvas, fragment_shader_descriptor, in_parameter_count, callback) { | |
// wrapped function constructor | |
internal__GPU_constructor(canvas, fragment_shader_descriptor, in_parameter_count, false, false, callback); | |
}; | |
function GPU_FloatsInFloatOut (canvas, fragment_shader_descriptor, in_parameter_count, callback) { | |
// wrapped function constructor | |
internal__GPU_constructor(canvas, fragment_shader_descriptor, in_parameter_count, false, false, callback); | |
}; | |
function internal__GPU_constructor (canvas, fragment_shader_descriptor, in_parameter_count, bytes_in_flag, bytes_out_flag, callback) { | |
// initialize/ensure webgl | |
GPU_Initialize_Static(canvas); | |
// load the fragment shader | |
var fragment_shader = internal__initialize_fragment_shader(fragment_shader_descriptor); | |
// build the shader program | |
var shader_program = internal__build_shader_program(GPU_Static.vertex_shader, fragment_shader); | |
// bake an object for making GPU calls | |
var ref = { | |
canvas: canvas, | |
shader_program: shader_program, | |
in_parameter_count: in_parameter_count, | |
bytes_in_flag: bytes_in_flag, | |
bytes_out_flag: bytes_out_flag, | |
tex: internal__setup_textures(in_parameter_count, bytes_in_flag), | |
output: null, | |
set_input_value: function (work_unit_index, parameter_index, value) { | |
// set the input value, calculating the texture index from the parameter index | |
ref.tex[Math.floor(parameter_index / 4)][work_unit_index] = value; | |
}, | |
prep_draw: function () { | |
console.log('prepping draw'); | |
// clear the context | |
GPU_Static.gl_context.clear(GPU_Static.gl_context.COLOR_BUFFER_BIT); checkGLError(); // do we actually need to though? | |
// use the shader program | |
GPU_Static.gl_context.useProgram(ref.shader_program); checkGLError(); | |
// bind the vertex buffer object | |
GPU_Static.gl_context.bindBuffer(GPU_Static.gl_context.ARRAY_BUFFER, GPU_Static.vertex_buffer_object); checkGLError(); | |
// enable attribute 0 (verticies) | |
GPU_Static.gl_context.enableVertexAttribArray(0); checkGLError(); | |
// mark verticies as represented by 3 component floats | |
GPU_Static.gl_context.vertexAttribPointer(0, 3, GPU_Static.gl_context.FLOAT, GPU_Static.gl_context.FALSE, 0, 0); checkGLError(); | |
// enable attribute 1 (texture coordinates (UVs)) | |
GPU_Static.gl_context.enableVertexAttribArray(1); checkGLError(); | |
// mark texture coordinates (UVs) as 2 component floats | |
GPU_Static.gl_context.vertexAttribPointer(1, 2, GPU_Static.gl_context.FLOAT, GPU_Static.gl_context.FALSE, 0, GPU_Static.gl_context.texture_coordinates_offset); checkGLError(); | |
// load time into shader | |
GPU_Static.gl_context.uniform1f(GPU_Static.gl_context.getUniformLocation(ref.shader_program, 'time'), new Date().getTime()); checkGLError(); | |
console.log('draw prepped'); | |
}, | |
bind_textures_to_GPU: function () { | |
console.log('binding textures to gpu'); | |
// iterate through textures | |
for (var t = 0; t < ref.tex.length; t++) { | |
console.log('binding texture ' + t + ' to gpu'); | |
// mark active texture | |
GPU_Static.gl_context.activeTexture(GPU_Static.gl_context.TEXTURE0 + t); checkGLError(); | |
// create a texture on the gpu | |
var texture = GPU_Static.gl_context.createTexture(); checkGLError(); | |
// bind the created texture | |
GPU_Static.gl_context.bindTexture(GPU_Static.gl_context.TEXTURE_2D, texture); checkGLError(); | |
// set texture wrap S to clamp to edge (this is critical for the pipeline to function) | |
GPU_Static.gl_context.texParameteri(GPU_Static.gl_context.TEXTURE_2D, GPU_Static.gl_context.TEXTURE_WRAP_S, GPU_Static.gl_context.CLAMP_TO_EDGE); checkGLError(); | |
// set texture wrap T to clamp to edge (this is critical for the pipeline to function) | |
GPU_Static.gl_context.texParameteri(GPU_Static.gl_context.TEXTURE_2D, GPU_Static.gl_context.TEXTURE_WRAP_T, GPU_Static.gl_context.CLAMP_TO_EDGE); checkGLError(); | |
// set texture minification filter to nearest (this is critical for the pipeline to function) | |
GPU_Static.gl_context.texParameteri(GPU_Static.gl_context.TEXTURE_2D, GPU_Static.gl_context.TEXTURE_MIN_FILTER, GPU_Static.gl_context.NEAREST); checkGLError(); | |
// set texture magnification to nearest (this is critical for the pipeline to function) | |
GPU_Static.gl_context.texParameteri(GPU_Static.gl_context.TEXTURE_2D, GPU_Static.gl_context.TEXTURE_MAG_FILTER, GPU_Static.gl_context.NEAREST); checkGLError(); | |
// set texture unpack alignment (this is critical for the pipeline to function) | |
GPU_Static.gl_context.pixelStorei(GPU_Static.gl_context.UNPACK_ALIGNMENT, 1); checkGLError(); | |
// set texture type (RGBA), size (width/height), type (float/byte), and copy texture buffer | |
GPU_Static.gl_context.texImage2D(GPU_Static.gl_context.TEXTURE_2D, 0, GPU_Static.gl_context.RGBA, ref.canvas.width, ref.canvas.height, 0, GPU_Static.gl_context.RGBA, ref.bytes_in_flag ? GPU_Static.gl_context.UNSIGNED_BYTE : GPU_Static.gl_context.FLOAT, ref.tex[t]); checkGLError(); | |
// get texture locator from context | |
var texture_location = GPU_Static.gl_context.getUniformLocation(ref.shader_program, 'tex' + t); checkGLError(); | |
// connect texture? | |
GPU_Static.gl_context.uniform1i(texture_location, t); checkGLError(); | |
} | |
console.log('bound ' + t + ' textures to gpu'); | |
}, | |
draw: function () { | |
console.log('drawing'); | |
// anticlimactic draw call | |
GPU_Static.gl_context.drawArrays(GPU_Static.gl_context.TRIANGLES, 0, 6); | |
// check for gl errors | |
checkGLError(); | |
// finish up our draw | |
GPU_Static.gl_context.finish(); | |
console.log('draw done'); | |
}, | |
read_bytes_out: function () { | |
console.log('reading bytes out'); | |
// allocate array of texture size by components RGBA | |
ref.output = new Uint8Array(ref.canvas.width * ref.canvas.height * 4); | |
// read output into output | |
GPU_Static.gl_context.readPixels(0, 0, ref.canvas.width, ref.canvas.height, GPU_Static.gl_context.RGBA, GPU_Static.gl_context.UNSIGNED_BYTE, ref.output); | |
console.log('read bytes out'); | |
}, | |
read_floats_out: function () { | |
// read output as bytes | |
ref.read_bytes_out(); | |
console.log('reading floats from bytes'); | |
// convert output to floats | |
ref.output = new Float32Array(ref.output.buffer); | |
console.log('read floats from bytes'); | |
} | |
}; | |
// return the baked GPU calling object | |
return callback(ref); | |
}; | |
function internal__load_shader (type, shader_source_string) { | |
// create the shader base | |
var shader = GPU_Static.gl_context.createShader(type); | |
// provide the shader source | |
GPU_Static.gl_context.shaderSource(shader, shader_source_string); | |
// attempt to compile the shader | |
GPU_Static.gl_context.compileShader(shader); | |
// if an error occured while compiling the shader | |
if (!GPU_Static.gl_context.getShaderParameter(shader, GPU_Static.gl_context.COMPILE_STATUS) || GPU_Static.gl_context.isContextLost()) { | |
// alert the user of the error | |
throw 'ERROR COMPILING SHADER:\n' + GPU_Static.gl_context.getShaderInfoLog(shader); | |
// attempt to cleanup the mess | |
GPU_Static.gl_context.deleteShader(shader); | |
// return no shader | |
return null; | |
} | |
// shader compiled successfully, return shader | |
return shader; | |
}; | |
function internal__initialize_fragment_shader (fragment_shader_descriptor) { | |
console.log('initializing fragment shader'); | |
// mustache swap in the user functions and main body | |
var fragment_shader = fragment_SHADER_STRING.split('{{user_functions}}').join(fragment_shader_descriptor.user_functions) | |
.split('{{body}}') .join(fragment_shader_descriptor.body); | |
// load the fragment shader | |
fragment_shader = internal__load_shader(GPU_Static.gl_context.FRAGMENT_SHADER, fragment_shader); | |
console.log('fragment shader initialized'); | |
// return the loaded shader | |
return fragment_shader; | |
}; | |
function internal__build_shader_program (vertex_shader, fragment_shader) { | |
// create the shader program | |
var shader_program = GPU_Static.gl_context.createProgram(); | |
// attach the vertex shader to the program | |
GPU_Static.gl_context.attachShader(shader_program, vertex_shader); | |
// attach the fragment shader to the program | |
GPU_Static.gl_context.attachShader(shader_program, fragment_shader); | |
// bind the position attribute location in the program | |
GPU_Static.gl_context.bindAttribLocation(shader_program, 0, 'g_Position'); | |
// bind the texture coordinate (UV) attribute location in the program | |
GPU_Static.gl_context.bindAttribLocation(shader_program, 1, 'g_TexCoord0'); | |
// perform the compilation/linkage of the programs | |
GPU_Static.gl_context.linkProgram(shader_program); | |
// check the result of linking the shader program | |
var result = GPU_Static.gl_context.getProgramParameter(shader_program, GPU_Static.gl_context.LINK_STATUS); | |
// if linking failed or we lost context | |
if (!result || GPU_Static.gl_context.isContextLost()) { | |
// alert the user there was an error | |
throw 'ERROR LINKED SHADER PROGRAM:\n' + GPU_Static.gl_context.getProgramInfoLog(shader_program); | |
// attempt to cleanup the program | |
GPU_Static.gl_context.deleteProgram(SHADER_PROGRAM); | |
// return no shader program | |
return null; | |
} | |
// linking suceeded, return linked shader program | |
return shader_program; | |
}; | |
function internal__setup_textures (in_parameter_count, bytes_in_flag) { | |
// ensure the bounding of the in parameters | |
if (in_parameter_count < 0 || in_parameter_count > 64) throw 'ERROR: in_parameter_count OUT OF BOUNDS (0-64)'; | |
// calculate the number of textures needed | |
var textures_needed = Math.ceil(in_parameter_count / 4); | |
// create an array to represent pixel components | |
var pixels = []; | |
// iterate over the width of the context canvas | |
for (var x = 0; x < GPU_Static.canvas.width; x++) { | |
// iterate over the height of the context canvas | |
for (var y = 0; y < GPU_Static.canvas.height; y++) { | |
// iterate over the components of a pixel RGBA | |
for (var r = 0; r < 4; r++) { | |
// add a pixel | |
pixels.push(0); | |
} | |
} | |
} | |
// create texture holder | |
var textures = []; | |
// iterate to create textures | |
for (var t = 0; t < textures_needed; t++) { | |
// if we are inputting bytes | |
if (bytes_in_flag) { | |
// add a byte texture | |
textures.push(new Uint8Array(pixels)); | |
// otherwise we are inputting floats | |
} else { | |
// add a float texture | |
textures.push(new Float32Array(pixels)); | |
} | |
} | |
// return created zero textures | |
return textures; | |
}; | |
</script> | |
</head> | |
<body style="padding-left: 10%; padding-top: 5%;" onload="main()"> | |
<div style="text-align: center"> | |
<canvas id="canvas" width="1024" height="1024"> | |
</canvas> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment