mirror of
https://github.com/gpujs/gpu.js.git
synced 2026-01-25 16:08:02 +00:00
2205 lines
79 KiB
JavaScript
2205 lines
79 KiB
JavaScript
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) <2013> <Roberto Gonzalez. http://stormcolour.appspot.com/>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
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 Software.
|
|
|
|
THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
/**
|
|
* Class for parallelization of calculations using the WebGL context similarly to webcl. This library use floating point texture capabilities (OES_texture_float)
|
|
* @class
|
|
* @constructor
|
|
* @param {WebGLRenderingContext} [webglcontext=undefined] your WebGLRenderingContext
|
|
*/
|
|
WebCLGL = function(webglcontext) {
|
|
this.utils = new WebCLGLUtils();
|
|
|
|
// WEBGL CONTEXT
|
|
this.e = undefined;
|
|
if(webglcontext == undefined) {
|
|
this.e = document.createElement('canvas');
|
|
this.e.width = 32;
|
|
this.e.height = 32;
|
|
this.gl = this.utils.getWebGLContextFromCanvas(this.e, {antialias: false});
|
|
} else this.gl = webglcontext;
|
|
|
|
this.gl.getExtension('OES_texture_float');
|
|
this.gl.getExtension('OES_texture_float_linear');
|
|
this.gl.getExtension('OES_element_index_uint');
|
|
|
|
var highPrecisionSupport = this.gl.getShaderPrecisionFormat(this.gl.FRAGMENT_SHADER, this.gl.HIGH_FLOAT);
|
|
this.precision = (highPrecisionSupport.precision != 0) ? 'precision highp float;\n\nprecision highp int;\n\n' : 'precision lowp float;\n\nprecision lowp int;\n\n';
|
|
|
|
|
|
|
|
// QUAD
|
|
var mesh = this.utils.loadQuad(undefined,1.0,1.0);
|
|
this.vertexBuffer_QUAD = this.gl.createBuffer();
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer_QUAD);
|
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(mesh.vertexArray), this.gl.STATIC_DRAW);
|
|
this.textureBuffer_QUAD = this.gl.createBuffer();
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer_QUAD);
|
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(mesh.textureArray), this.gl.STATIC_DRAW);
|
|
this.indexBuffer_QUAD = this.gl.createBuffer();
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer_QUAD);
|
|
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(mesh.indexArray), this.gl.STATIC_DRAW);
|
|
|
|
|
|
|
|
// SHADER READPIXELS
|
|
var sourceVertex = this.precision+
|
|
'attribute vec3 aVertexPosition;\n'+
|
|
'attribute vec2 aTextureCoord;\n'+
|
|
|
|
'varying vec2 vTextureCoord;\n'+
|
|
|
|
'void main(void) {\n'+
|
|
'gl_Position = vec4(aVertexPosition, 1.0);\n'+
|
|
'vTextureCoord = aTextureCoord;\n'+
|
|
'}\n';
|
|
var sourceFragment = this.precision+
|
|
'uniform sampler2D sampler_buffer;\n'+
|
|
|
|
'uniform int u_vectorValue;\n'+
|
|
'uniform int u_offset;\n'+
|
|
|
|
'varying vec2 vTextureCoord;\n'+
|
|
|
|
this.utils.packGLSLFunctionString()+
|
|
|
|
'void main(void) {\n'+
|
|
'vec4 tex = texture2D(sampler_buffer, vTextureCoord);'+
|
|
'if(u_offset > 0) {'+
|
|
'float offset = float(u_offset);'+
|
|
'if(u_vectorValue == 0) gl_FragColor = pack((tex.r+offset)/(offset*2.0));\n'+
|
|
'if(u_vectorValue == 1) gl_FragColor = pack((tex.g+offset)/(offset*2.0));\n'+
|
|
'if(u_vectorValue == 2) gl_FragColor = pack((tex.b+offset)/(offset*2.0));\n'+
|
|
'if(u_vectorValue == 3) gl_FragColor = pack((tex.a+offset)/(offset*2.0));\n'+
|
|
'} else {'+
|
|
'if(u_vectorValue == 0) gl_FragColor = pack(tex.r);\n'+
|
|
'if(u_vectorValue == 1) gl_FragColor = pack(tex.g);\n'+
|
|
'if(u_vectorValue == 2) gl_FragColor = pack(tex.b);\n'+
|
|
'if(u_vectorValue == 3) gl_FragColor = pack(tex.a);\n'+
|
|
'}'+
|
|
'}\n';
|
|
|
|
this.shader_readpixels = this.gl.createProgram();
|
|
this.utils.createShader(this.gl, "CLGLREADPIXELS", sourceVertex, sourceFragment, this.shader_readpixels);
|
|
|
|
this.u_offset = this.gl.getUniformLocation(this.shader_readpixels, "u_offset");
|
|
this.u_vectorValue = this.gl.getUniformLocation(this.shader_readpixels, "u_vectorValue");
|
|
|
|
this.sampler_buffer = this.gl.getUniformLocation(this.shader_readpixels, "sampler_buffer");
|
|
|
|
this.attr_VertexPos = this.gl.getAttribLocation(this.shader_readpixels, "aVertexPosition");
|
|
this.attr_TextureCoord = this.gl.getAttribLocation(this.shader_readpixels, "aTextureCoord");
|
|
|
|
|
|
|
|
// SHADER COPYTEXTURE
|
|
var sourceVertex = this.precision+
|
|
'attribute vec3 aVertexPosition;\n'+
|
|
'attribute vec2 aTextureCoord;\n'+
|
|
|
|
'varying vec2 vTextureCoord;\n'+
|
|
|
|
'void main(void) {\n'+
|
|
'gl_Position = vec4(aVertexPosition, 1.0);\n'+
|
|
'vTextureCoord = aTextureCoord;\n'+
|
|
'}';
|
|
var sourceFragment = this.precision+
|
|
|
|
'uniform sampler2D sampler_toSave;\n'+
|
|
|
|
'varying vec2 vTextureCoord;\n'+
|
|
|
|
'void main(void) {\n'+
|
|
'vec4 texture = texture2D(sampler_toSave, vTextureCoord);\n'+
|
|
'gl_FragColor = texture;'+
|
|
'}';
|
|
this.shader_copyTexture = this.gl.createProgram();
|
|
this.utils.createShader(this.gl, "CLGLCOPYTEXTURE", sourceVertex, sourceFragment, this.shader_copyTexture);
|
|
|
|
this.attr_copyTexture_pos = this.gl.getAttribLocation(this.shader_copyTexture, "aVertexPosition");
|
|
this.attr_copyTexture_tex = this.gl.getAttribLocation(this.shader_copyTexture, "aTextureCoord");
|
|
|
|
this.sampler_copyTexture_toSave = this.gl.getUniformLocation(this.shader_copyTexture, "sampler_toSave");
|
|
};
|
|
|
|
/**
|
|
* Copy one WebCLGLBuffer|WebGLTexture to another WebCLGLBuffer|WebGLTexture.
|
|
* @param {WebCLGLBuffer|WebGLTexture} valueToRead The buffer to read.
|
|
* @param {WebCLGLBuffer|WebGLTexture} valueToWrite The buffer to write.
|
|
* @example
|
|
* // This is useful if you need to write about a buffer and also want to read it by passing it as an argument in main().
|
|
* // If this is the case, you have to create a temporary buffer for the writing and take the original buffer for the reading:
|
|
* kernelA.setKernelArg (x, ORIGINALbuffer);
|
|
* webCLGL.enqueueNDRangeKernel (kernelA, TMPbuffer);
|
|
* kernelB.setKernelArg (x, ORIGINALbuffer);
|
|
* webCLGL.enqueueNDRangeKernel (kernelB, anotherBuffer);
|
|
* // Then overwrite the original with the temporary:
|
|
* webCLGL.copyTexture (TMPbuffer, ORIGINALbuffer);
|
|
*/
|
|
WebCLGL.prototype.copy = function(valuesToRead, valuesToWrite) {
|
|
if(valuesToRead instanceof WebCLGLBuffer) {
|
|
for(var i=0; i < valuesToRead.items.length; i++) {
|
|
valueToRead = valuesToRead.items[i];
|
|
valueToWrite = valuesToWrite.items[i];
|
|
|
|
this.copyItem(valueToRead, valueToWrite);
|
|
}
|
|
} else if(valuesToRead instanceof WebGLTexture) // WebGLTexture
|
|
this.copyItem(valuesToRead, valuesToWrite);
|
|
};
|
|
WebCLGL.prototype.copyItem = function(valueToRead, valueToWrite) {
|
|
if(valueToRead instanceof WebCLGLBufferItem) {
|
|
this.gl.viewport(0, 0, valueToWrite.W, valueToWrite.H);
|
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, valueToWrite.fBuffer);
|
|
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, valueToWrite.textureData, 0);
|
|
} else if(valueToRead instanceof WebGLTexture)
|
|
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, valueToWrite, 0);
|
|
|
|
|
|
this.gl.useProgram(this.shader_copyTexture);
|
|
|
|
this.gl.activeTexture(this.gl.TEXTURE0);
|
|
var toRead = (valueToRead instanceof WebGLTexture) ? valueToRead : valueToRead.textureData;
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, toRead);
|
|
this.gl.uniform1i(this.sampler_copyTexture_toSave, 0);
|
|
|
|
|
|
this.gl.enableVertexAttribArray(this.attr_copyTexture_pos);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(this.attr_copyTexture_pos, 3, this.gl.FLOAT, false, 0, 0);
|
|
|
|
this.gl.enableVertexAttribArray(this.attr_copyTexture_tex);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(this.attr_copyTexture_tex, 3, this.gl.FLOAT, false, 0, 0);
|
|
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer_QUAD);
|
|
this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0);
|
|
};
|
|
|
|
/**
|
|
* Create a empty WebCLGLBuffer
|
|
* @param {Int|Array<Float>} length Length of buffer or Array with width and height values if is for a WebGLTexture
|
|
* @param {String} [type="FLOAT"] type FLOAT4 OR FLOAT
|
|
* @param {Int} [offset=0] If 0 the range is from 0.0 to 1.0 else if >0 then the range is from -offset.0 to offset.0
|
|
* @param {Bool} [linear=false] linear texParameteri type for the WebGLTexture
|
|
* @param {String} [mode="FRAGMENT"] Mode for this buffer. "FRAGMENT", "VERTEX", "VERTEX_INDEX", "VERTEX_FROM_KERNEL", "VERTEX_AND_FRAGMENT"
|
|
* @param {Array} [splits=[length]] Splits length for this buffer.
|
|
* @returns {WebCLGLBuffer}
|
|
*/
|
|
WebCLGL.prototype.createBuffer = function(length, type, offset, linear, mode, splits) {
|
|
return new WebCLGLBuffer(this.gl, length, type, offset, linear, mode, splits);
|
|
};
|
|
|
|
/**
|
|
* Create a kernel
|
|
* @returns {WebCLGLKernel}
|
|
* @param {String} [source=undefined]
|
|
* @param {String} [header=undefined] Additional functions
|
|
*/
|
|
WebCLGL.prototype.createKernel = function(source, header) {
|
|
var webclglKernel = new WebCLGLKernel(this.gl, source, header);
|
|
return webclglKernel;
|
|
};
|
|
|
|
/**
|
|
* Create a vertex and fragment programs for a WebGL graphical representation after some enqueueNDRangeKernel
|
|
* @returns {WebCLGLVertexFragmentProgram}
|
|
* @param {String} [vertexSource=undefined]
|
|
* @param {String} [vertexHeader=undefined]
|
|
* @param {String} [fragmentSource=undefined]
|
|
* @param {String} [fragmentHeader=undefined]
|
|
*/
|
|
WebCLGL.prototype.createVertexFragmentProgram = function(vertexSource, vertexHeader, fragmentSource, fragmentHeader) {
|
|
var webclglVertexFragmentProgram = new WebCLGLVertexFragmentProgram(this.gl, vertexSource, vertexHeader, fragmentSource, fragmentHeader);
|
|
return webclglVertexFragmentProgram;
|
|
};
|
|
|
|
/**
|
|
* Create work
|
|
* @returns {WebCLGLWork}
|
|
*/
|
|
WebCLGL.prototype.createWork = function(offset) {
|
|
var webclglWork = new WebCLGLWork(this, offset);
|
|
return webclglWork;
|
|
};
|
|
|
|
/**
|
|
* Write on buffer
|
|
* @type Void
|
|
* @param {WebCLGLBuffer} buffer
|
|
* @param {Array|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} array
|
|
* @param {Bool} [flip=false]
|
|
*/
|
|
WebCLGL.prototype.enqueueWriteBuffer = function(buffer, arr, flip) {
|
|
if(buffer.mode == "FRAGMENT" || buffer.mode == "VERTEX_FROM_KERNEL" || buffer.mode == "VERTEX_AND_FRAGMENT") {
|
|
buffer.writeWebGLTextureBuffer(arr, flip);
|
|
}
|
|
if(buffer.mode == "VERTEX" || buffer.mode == "VERTEX_INDEX" || buffer.mode == "VERTEX_FROM_KERNEL" || buffer.mode == "VERTEX_AND_FRAGMENT") {
|
|
buffer.writeWebGLBuffer(arr, flip);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Perform calculation and save the result on a WebCLGLBuffer
|
|
* @param {WebCLGLKernel} webCLGLKernel
|
|
* @param {WebCLGLBuffer} [webCLGLBuffer=undefined]
|
|
* @param {Int} [geometryLength=1] - Length of geometry (1 for points, 3 for triangles...)
|
|
*/
|
|
WebCLGL.prototype.enqueueNDRangeKernel = function(webCLGLKernel, webCLGLBuffers, geometryLength) {
|
|
if(webCLGLBuffers != undefined) {
|
|
for(var i=0; i < webCLGLBuffers.items.length; i++) {
|
|
webCLGLBuffer = webCLGLBuffers.items[i];
|
|
|
|
this.gl.viewport(0, 0, webCLGLBuffer.W, webCLGLBuffer.H);
|
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, webCLGLBuffer.fBuffer);
|
|
this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, this.gl.TEXTURE_2D, webCLGLBuffer.textureData, 0);
|
|
|
|
this.enqueueNDRangeKernelNow(webCLGLKernel, i, geometryLength);
|
|
}
|
|
} else {
|
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
|
|
this.enqueueNDRangeKernelNow(webCLGLKernel, 0, geometryLength);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @param {WebCLGLKernel} webCLGLKernel
|
|
* @param {Int} item
|
|
* @param {Int} [geometryLength=1] - Length of geometry (1 for points, 3 for triangles...)
|
|
*/
|
|
WebCLGL.prototype.enqueueNDRangeKernelNow = function(webCLGLKernel, i, geometryLength) {
|
|
var kp = webCLGLKernel.kernelPrograms[0];
|
|
this.gl.useProgram(kp.kernel);
|
|
|
|
var _geometryLength = (geometryLength != undefined) ? geometryLength : 1;
|
|
this.gl.uniform1f(kp.uGeometryLength, _geometryLength);
|
|
|
|
var currentTextureUnit = 0;
|
|
for(var n = 0, f = kp.samplers.length; n < f; n++) {
|
|
var ks = kp.samplers[n];
|
|
|
|
if(currentTextureUnit < 16)
|
|
this.gl.activeTexture(this.gl["TEXTURE"+currentTextureUnit]);
|
|
else
|
|
this.gl.activeTexture(this.gl["TEXTURE16"]);
|
|
|
|
if(ks.value != undefined) {
|
|
var item = (ks.value.items[i] != undefined) ? ks.value.items[i] : ks.value.items[0];
|
|
this.gl.uniform1f(kp.uBufferWidth, item.W);
|
|
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, item.textureData);
|
|
this.gl.uniform1i(ks.location[0], currentTextureUnit);
|
|
} else {
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
}
|
|
currentTextureUnit++;
|
|
}
|
|
for(var n = 0, f = kp.uniforms.length; n < f; n++) {
|
|
var ku = kp.uniforms[n];
|
|
if(ku.value != undefined)
|
|
if(ku.type == 'float')
|
|
this.gl.uniform1f(ku.location[0], ku.value);
|
|
else if(ku.type == 'float4')
|
|
this.gl.uniform4f(ku.location[0], ku.value[0], ku.value[1], ku.value[2], ku.value[3]);
|
|
else if(ku.type == 'mat4')
|
|
this.gl.uniformMatrix4fv(ku.location[0], false, ku.value);
|
|
}
|
|
|
|
|
|
this.gl.enableVertexAttribArray(kp.attr_VertexPos);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(kp.attr_VertexPos, 3, this.gl.FLOAT, false, 0, 0);
|
|
|
|
this.gl.enableVertexAttribArray(kp.attr_TextureCoord);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(kp.attr_TextureCoord, 3, this.gl.FLOAT, false, 0, 0);
|
|
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer_QUAD);
|
|
this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0);
|
|
};
|
|
|
|
/**
|
|
* Perform WebGL graphical representation
|
|
* @param {WebCLGLVertexFragmentProgram} webCLGLVertexFragmentProgram
|
|
* @param {WebCLGLBuffer} buffer Buffer to draw type (type indices or vertex)
|
|
* @param {Int} [drawMode=4] 0=POINTS, 3=LINE_STRIP, 2=LINE_LOOP, 1=LINES, 5=TRIANGLE_STRIP, 6=TRIANGLE_FAN and 4=TRIANGLES
|
|
* @param {Int} [geometryLength=1] - Length of geometry (1 for points, 3 for triangles...)
|
|
*/
|
|
WebCLGL.prototype.enqueueVertexFragmentProgram = function(webCLGLVertexFragmentProgram, buffer, drawMode, geometryLength) {
|
|
var Dmode = (drawMode != undefined) ? drawMode : 4;
|
|
var _geometryLength = (geometryLength != undefined) ? geometryLength : 1;
|
|
|
|
this.gl.useProgram(webCLGLVertexFragmentProgram.vertexFragmentProgram);
|
|
|
|
if(webCLGLVertexFragmentProgram.vertexAttributes[0].value != undefined) {
|
|
for(var i=0; i < webCLGLVertexFragmentProgram.vertexAttributes[0].value.items.length; i++) {
|
|
var bufferItem = webCLGLVertexFragmentProgram.vertexAttributes[0].value.items[i];
|
|
|
|
this.gl.uniform1f(webCLGLVertexFragmentProgram.uOffset, bufferItem.offset);
|
|
this.gl.uniform1f(webCLGLVertexFragmentProgram.uGeometryLength, _geometryLength);
|
|
|
|
var currentTextureUnit = 0;
|
|
for(var n = 0, f = webCLGLVertexFragmentProgram.vertexAttributes.length; n < f; n++) {
|
|
var va = webCLGLVertexFragmentProgram.vertexAttributes[n];
|
|
if(va.value != undefined) {
|
|
var item = (va.value.items[i] != undefined) ? va.value.items[i] : va.value.items[0];
|
|
|
|
|
|
if(va.type == 'buffer_float4_fromKernel' || va.type == 'buffer_float_fromKernel') {
|
|
if(currentTextureUnit < 16)
|
|
this.gl.activeTexture(this.gl["TEXTURE"+currentTextureUnit]);
|
|
else
|
|
this.gl.activeTexture(this.gl["TEXTURE16"]);
|
|
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, item.textureData);
|
|
this.gl.uniform1i(va.location[0], currentTextureUnit);
|
|
|
|
this.gl.uniform1f(webCLGLVertexFragmentProgram.uBufferWidth, item.W);
|
|
|
|
currentTextureUnit++;
|
|
} else if(va.type == 'buffer_float4') {
|
|
this.gl.enableVertexAttribArray(va.location[0]);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, item.vertexData0);
|
|
this.gl.vertexAttribPointer(va.location[0], 4, this.gl.FLOAT, false, 0, 0);
|
|
} else if(va.type == 'buffer_float') {
|
|
this.gl.enableVertexAttribArray(va.location[0]);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, item.vertexData0);
|
|
this.gl.vertexAttribPointer(va.location[0], 1, this.gl.FLOAT, false, 0, 0);
|
|
}
|
|
} else {
|
|
if(va.type == 'buffer_float4_fromKernel' || va.type == 'buffer_float_fromKernel') {
|
|
if(currentTextureUnit < 16)
|
|
this.gl.activeTexture(this.gl["TEXTURE"+currentTextureUnit]);
|
|
else
|
|
this.gl.activeTexture(this.gl["TEXTURE16"]);
|
|
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
|
|
currentTextureUnit++;
|
|
} else if(va.type == 'buffer_float4') {
|
|
this.gl.disableVertexAttribArray(va.location[0]);
|
|
} else if(va.type == 'buffer_float') {
|
|
this.gl.disableVertexAttribArray(va.location[0]);
|
|
}
|
|
}
|
|
}
|
|
//var currentTextureUnit = 0;
|
|
for(var n = 0, f = webCLGLVertexFragmentProgram.fragmentSamplers.length; n < f; n++) {
|
|
var fs = webCLGLVertexFragmentProgram.fragmentSamplers[n];
|
|
|
|
if(currentTextureUnit < 16)
|
|
this.gl.activeTexture(this.gl["TEXTURE"+currentTextureUnit]);
|
|
else
|
|
this.gl.activeTexture(this.gl["TEXTURE16"]);
|
|
|
|
if(fs.value != undefined) {
|
|
var item = (fs.value.items[i] != undefined) ? fs.value.items[i] : fs.value.items[0];
|
|
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, item.textureData);
|
|
this.gl.uniform1i(fs.location[0], currentTextureUnit);
|
|
} else {
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
|
|
}
|
|
currentTextureUnit++;
|
|
}
|
|
|
|
for(var n = 0, f = webCLGLVertexFragmentProgram.vertexUniforms.length; n < f; n++) {
|
|
var vu = webCLGLVertexFragmentProgram.vertexUniforms[n];
|
|
if(vu.value != undefined) {
|
|
if(vu.type == 'float')
|
|
this.gl.uniform1f(vu.location[0], vu.value);
|
|
else if(vu.type == 'float4')
|
|
this.gl.uniform4f(vu.location[0], vu.value[0], vu.value[1], vu.value[2], vu.value[3]);
|
|
else if(vu.type == 'mat4')
|
|
this.gl.uniformMatrix4fv(vu.location[0], false, vu.value);
|
|
}
|
|
}
|
|
|
|
for(var n = 0, f = webCLGLVertexFragmentProgram.fragmentUniforms.length; n < f; n++) {
|
|
var fu = webCLGLVertexFragmentProgram.fragmentUniforms[n];
|
|
if(fu.value != undefined) {
|
|
if(fu.type == 'float')
|
|
this.gl.uniform1f(fu.location[0], fu.value);
|
|
else if(fu.type == 'float4')
|
|
this.gl.uniform4f(fu.location[0], fu.value[0], fu.value[1], fu.value[2], fu.value[3]);
|
|
else if(fu.type == 'mat4')
|
|
this.gl.uniformMatrix4fv(fu.location[0], false, fu.value);
|
|
}
|
|
}
|
|
|
|
if(buffer.mode == "VERTEX_INDEX") {
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, buffer.items[i].vertexData0);
|
|
this.gl.drawElements(Dmode, buffer.items[i].length, this.gl.UNSIGNED_SHORT, 0);
|
|
} else {
|
|
this.gl.drawArrays(Dmode, 0, buffer.items[i].length);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the internally WebGLTexture (type FLOAT), if the WebGLRenderingContext was given.
|
|
* @returns {WebGLTexture}
|
|
*/
|
|
WebCLGL.prototype.enqueueReadBuffer_WebGLTexture = function(buffer) {
|
|
return buffer.items[0].textureData;
|
|
};
|
|
|
|
/**
|
|
* Get RGBAUint8Array array from a WebCLGLBuffer <br>
|
|
* Read buffer in a specifics WebGL 32bit channel and return the data in one array of packets RGBA_Uint8Array <br>
|
|
* @param {WebCLGLBuffer} buffer
|
|
* @param {Int} channel Channel to read
|
|
* @returns {Uint8Array}
|
|
**/
|
|
WebCLGL.prototype.enqueueReadBuffer = function(buffer, item) {
|
|
this.gl.uniform1i(this.u_vectorValue, item);
|
|
|
|
this.gl.uniform1i(this.u_offset, buffer.offset);
|
|
|
|
this.gl.activeTexture(this.gl.TEXTURE0);
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, buffer.textureData);
|
|
this.gl.uniform1i(this.sampler_buffer, 0);
|
|
|
|
|
|
this.gl.enableVertexAttribArray(this.attr_VertexPos);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(this.attr_VertexPos, 3, buffer._supportFormat, false, 0, 0);
|
|
|
|
this.gl.enableVertexAttribArray(this.attr_TextureCoord);
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer_QUAD);
|
|
this.gl.vertexAttribPointer(this.attr_TextureCoord, 3, buffer._supportFormat, false, 0, 0);
|
|
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer_QUAD);
|
|
this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0);
|
|
|
|
var arrLength = buffer.length*4;
|
|
if(item == 0) {
|
|
this.gl.readPixels(0, 0, buffer.W, buffer.H, this.gl.RGBA, this.gl.UNSIGNED_BYTE, buffer.outArray4Uint8ArrayX);
|
|
return buffer.outArray4Uint8ArrayX.slice(0, arrLength);
|
|
} else if(item == 1) {
|
|
this.gl.readPixels(0, 0, buffer.W, buffer.H, this.gl.RGBA, this.gl.UNSIGNED_BYTE, buffer.outArray4Uint8ArrayY);
|
|
return buffer.outArray4Uint8ArrayY.slice(0, arrLength);
|
|
} else if(item == 2) {
|
|
this.gl.readPixels(0, 0, buffer.W, buffer.H, this.gl.RGBA, this.gl.UNSIGNED_BYTE, buffer.outArray4Uint8ArrayZ);
|
|
return buffer.outArray4Uint8ArrayZ.slice(0, arrLength);
|
|
} else if(item == 3) {
|
|
this.gl.readPixels(0, 0, buffer.W, buffer.H, this.gl.RGBA, this.gl.UNSIGNED_BYTE, buffer.outArray4Uint8ArrayW);
|
|
return buffer.outArray4Uint8ArrayW.slice(0, arrLength);
|
|
}
|
|
};
|
|
|
|
/** @private **/
|
|
WebCLGL.prototype.prepareViewportForBufferRead = function(buffer) {
|
|
this.gl.viewport(0, 0, buffer.W, buffer.H);
|
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
|
|
if(this.e != undefined) {
|
|
this.e.width = buffer.W;
|
|
this.e.height = buffer.H;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get 4 RGBAUint8Array arrays from a WebCLGLBuffer type FLOAT4 <br>
|
|
* Internally performs four calls to enqueueReadBuffer and return the data in one array of four packets RGBA_Uint8Array
|
|
* @param {WebCLGLBuffer} buffer
|
|
**/
|
|
WebCLGL.prototype.enqueueReadBuffer_Packet4Uint8Array_Float4 = function(buffers) {
|
|
if(buffers.items[0].type == "FLOAT4") {
|
|
for(var i=0; i < buffers.items.length; i++) {
|
|
buffer = buffers.items[i];
|
|
|
|
this.prepareViewportForBufferRead(buffer);
|
|
this.gl.useProgram(this.shader_readpixels);
|
|
|
|
buffer.Packet4Uint8Array_Float4 = [ this.enqueueReadBuffer(buffer, 0),
|
|
this.enqueueReadBuffer(buffer, 1),
|
|
this.enqueueReadBuffer(buffer, 2),
|
|
this.enqueueReadBuffer(buffer, 3)];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get 4 Float32Array arrays from a WebCLGLBuffer type FLOAT4 <br>
|
|
* Internally performs one calls to enqueueReadBuffer and return the data in one array of four Float32Array
|
|
* @param {WebCLGLBuffer} buffer
|
|
* @returns {Array<Array>}
|
|
*/
|
|
WebCLGL.prototype.enqueueReadBuffer_Float4 = function(buffers) {
|
|
var Float4_Un = [[],[],[],[]];
|
|
if(buffers.items[0].type == "FLOAT4") {
|
|
for(var i=0; i < buffers.items.length; i++) {
|
|
buffer = buffers.items[i];
|
|
|
|
this.prepareViewportForBufferRead(buffer);
|
|
this.gl.useProgram(this.shader_readpixels);
|
|
|
|
buffer.Packet4Uint8Array_Float4 = [ this.enqueueReadBuffer(buffer, 0),
|
|
this.enqueueReadBuffer(buffer, 1),
|
|
this.enqueueReadBuffer(buffer, 2),
|
|
this.enqueueReadBuffer(buffer, 3)];
|
|
buffer.Float4 = [];
|
|
|
|
for(var n=0, fn= 4; n < fn; n++) {
|
|
var arr = buffer.Packet4Uint8Array_Float4[n];
|
|
|
|
var outArrayFloat32Array = new Float32Array((buffer.W*buffer.H));
|
|
for(var nb = 0, fnb = arr.length/4; nb < fnb; nb++) {
|
|
var idd = nb*4;
|
|
if(buffer.offset>0) outArrayFloat32Array[nb] = (this.utils.unpack([arr[idd+0]/255,
|
|
arr[idd+1]/255,
|
|
arr[idd+2]/255,
|
|
arr[idd+3]/255])*(buffer.offset*2))-buffer.offset;
|
|
else outArrayFloat32Array[nb] = (this.utils.unpack([ arr[idd+0]/255,
|
|
arr[idd+1]/255,
|
|
arr[idd+2]/255,
|
|
arr[idd+3]/255]));
|
|
Float4_Un[n].push(outArrayFloat32Array[nb]);
|
|
}
|
|
|
|
buffer.Float4.push(outArrayFloat32Array.slice(0, buffer.length));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Float4_Un;
|
|
};
|
|
|
|
/**
|
|
* Get 1 RGBAUint8Array array from a WebCLGLBuffer type FLOAT <br>
|
|
* Internally performs one call to enqueueReadBuffer and return the data in one array of one packets RGBA_Uint8Array
|
|
* @param {WebCLGLBuffer} buffer
|
|
*
|
|
* @example
|
|
* // Unpack in your shader to float with:
|
|
* float unpack (vec4 4Uint8Array) {
|
|
* const vec4 bitShifts = vec4(1.0,1.0 / 255.0, 1.0 / (255.0 * 255.0), 1.0 / (255.0 * 255.0 * 255.0));
|
|
* return dot(4Uint8Array, bitShifts);
|
|
* }
|
|
* float offset = "OFFSET OF BUFFER";
|
|
* vec4 4Uint8Array = atributeFloatInPacket4Uint8Array; // IF UNPACK IN VERTEX PROGRAM
|
|
* vec4 4Uint8Array = texture2D(samplerFloatInPacket4Uint8Array, vTextureScreenCoord); // IF UNPACK IN FRAGMENT PROGRAM
|
|
* float value = (offset > 0.0) ? (unpack(4Uint8Array)*(offset*2.0))-offset : unpack(4Uint8Array);
|
|
*
|
|
* // JAVASCRIPT IF UNPACK IN VERTEX PROGRAM
|
|
* attr_FloatInPacket4Uint8Array = gl.getAttribLocation(shaderProgram, "atributeFloatInPacket4Uint8Array");
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, webGLBufferObject);
|
|
* gl.bufferSubData(gl.ARRAY_BUFFER, 0, webCLGL.enqueueReadBuffer_Packet4Uint8Array_Float(buffer_XX)[0]);
|
|
* gl.vertexAttribPointer(attr_FloatInPacket4Uint8Array, 4, gl.UNSIGNED_BYTE, true, 0, 0); // true for normalize
|
|
*
|
|
* // JAVASCRIPT IF UNPACK IN FRAGMENT PROGRAM
|
|
* sampler_FloatInPacket4Uint8Array = gl.getUniformLocation(shaderProgram, "samplerFloatInPacket4Uint8Array");
|
|
* gl.activeTexture(gl.TEXTURE0);
|
|
* gl.bindTexture(gl.TEXTURE_2D, webGLTextureObject);
|
|
* gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewportWidth,viewportHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, webCLGL.enqueueReadBuffer_Packet4Uint8Array_Float(buffer_XX)[0]);
|
|
* gl.uniform1i(sampler_FloatInPacket4Uint8Array, 0);
|
|
*/
|
|
WebCLGL.prototype.enqueueReadBuffer_Packet4Uint8Array_Float = function(buffers) {
|
|
if(buffers.items[0].type == "FLOAT") {
|
|
for(var i=0; i < buffers.items.length; i++) {
|
|
buffer = buffers.items[i];
|
|
|
|
this.prepareViewportForBufferRead(buffer);
|
|
this.gl.useProgram(this.shader_readpixels);
|
|
|
|
buffer.Packet4Uint8Array_Float = [this.enqueueReadBuffer(buffer, 0)];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get 1 Float32Array array from a WebCLGLBuffer type FLOAT <br>
|
|
* Internally performs one calls to enqueueReadBuffer and return the data in one array of one Float32Array
|
|
* @param {WebCLGLBuffer} buffer
|
|
* @returns {Array<Array>}
|
|
*/
|
|
WebCLGL.prototype.enqueueReadBuffer_Float = function(buffers) {
|
|
var Float_Un = [[]];
|
|
if(buffers.items[0].type == "FLOAT") {
|
|
for(var i=0; i < buffers.items.length; i++) {
|
|
buffer = buffers.items[i];
|
|
|
|
this.prepareViewportForBufferRead(buffer);
|
|
this.gl.useProgram(this.shader_readpixels);
|
|
|
|
buffer.Packet4Uint8Array_Float = [this.enqueueReadBuffer(buffer, 0)];
|
|
buffer.Float = [];
|
|
|
|
for(var n=0, fn= 1; n < fn; n++) {
|
|
var arr = buffer.Packet4Uint8Array_Float[n];
|
|
|
|
var outArrayFloat32Array = new Float32Array((buffer.W*buffer.H));
|
|
for(var nb = 0, fnb = arr.length/4; nb < fnb; nb++) {
|
|
var idd = nb*4;
|
|
if(buffer.offset>0) outArrayFloat32Array[nb] = (this.utils.unpack([arr[idd+0]/255,
|
|
arr[idd+1]/255,
|
|
arr[idd+2]/255,
|
|
arr[idd+3]/255])*(buffer.offset*2))-buffer.offset;
|
|
else outArrayFloat32Array[nb] = (this.utils.unpack([ arr[idd+0]/255,
|
|
arr[idd+1]/255,
|
|
arr[idd+2]/255,
|
|
arr[idd+3]/255]));
|
|
Float_Un[n].push(outArrayFloat32Array[nb]);
|
|
}
|
|
|
|
buffer.Float.push(outArrayFloat32Array.slice(0, buffer.length));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Float_Un;
|
|
};
|
|
/**
|
|
* WebCLGLBuffer Object
|
|
* @class
|
|
* @constructor
|
|
* @property {Float} length
|
|
*/
|
|
WebCLGLBuffer = function(gl, length, type, offset, linear, mode, splits) {
|
|
this.gl = gl;
|
|
this.length = (length.constructor === Array) ? [length[0],length[1]] : length;
|
|
this.type = type;
|
|
this.offset = offset;
|
|
this.linear = linear;
|
|
this.mode = (mode != undefined) ? mode : "FRAGMENT"; // "FRAGMENT", "VERTEX", "VERTEX_INDEX", "VERTEX_FROM_KERNEL", "VERTEX_AND_FRAGMENT"
|
|
this.splits = (splits != undefined) ? splits : [this.length];
|
|
|
|
this.items = [];
|
|
var countArr = this.length;
|
|
currItem = 0;
|
|
if(this.length.constructor !== Array) {
|
|
while(true) {
|
|
var spl = (currItem == 0) ? this.splits[currItem] : this.splits[currItem]-this.splits[currItem-1];
|
|
if(countArr > spl) {
|
|
this.items[currItem] = new WebCLGLBufferItem(gl, spl, type, offset, linear, mode);
|
|
countArr -= spl;
|
|
} else {
|
|
this.items[currItem] = new WebCLGLBufferItem(gl, countArr, type, offset, linear, mode);
|
|
countArr -= countArr;
|
|
}
|
|
if(countArr <= 0) break;
|
|
currItem++;
|
|
}
|
|
} else {
|
|
this.items[currItem] = new WebCLGLBufferItem(gl, this.length, type, offset, linear, mode);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Write WebGLTexture buffer
|
|
* @param {Array|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} array
|
|
* @param {Bool} [flip=false]
|
|
* @type Void
|
|
*/
|
|
WebCLGLBuffer.prototype.writeWebGLTextureBuffer = function(arr, flip) {
|
|
var m = (this.type == "FLOAT4") ? 4 : 1;
|
|
for(var i=0; i < this.items.length; i++) {
|
|
var startItem = (i==0) ? 0 : this.splits[i-1]*m;
|
|
var endItem = startItem+(this.items[i].length*m);
|
|
|
|
if(this.items.length > 1)
|
|
this.items[i].writeWebGLTextureBuffer(arr.slice(startItem, endItem), flip);
|
|
else
|
|
this.items[i].writeWebGLTextureBuffer(arr, flip);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Write WebGL buffer
|
|
* @param {Array|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} array
|
|
* @param {Bool} [flip=false]
|
|
* @type Void
|
|
*/
|
|
WebCLGLBuffer.prototype.writeWebGLBuffer = function(arr, flip) {
|
|
var m = (this.type == "FLOAT4") ? 4 : 1;
|
|
for(var i=0; i < this.items.length; i++) {
|
|
var startItem = (i==0) ? 0 : this.splits[i-1]*m;
|
|
var endItem = startItem+(this.items[i].length*m);
|
|
|
|
if(this.items.length > 1)
|
|
this.items[i].writeWebGLBuffer(arr.slice(startItem, endItem), flip);
|
|
else
|
|
this.items[i].writeWebGLBuffer(arr, flip);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Remove this buffer
|
|
* @type Void
|
|
*/
|
|
WebCLGLBuffer.prototype.remove = function() {
|
|
for(var n=0; n < this.items.length; n++) {
|
|
this.items[n].remove();
|
|
}
|
|
};/**
|
|
* WebCLGLBuffer Object
|
|
* @class
|
|
* @constructor
|
|
* @property {WebGLTexture} textureData
|
|
* @property {Array<Float>} inData Original array
|
|
* @property {Int} [offset=0] offset of buffer
|
|
*/
|
|
WebCLGLBufferItem = function(gl, length, type, offset, linear, mode) {
|
|
this.gl = gl;
|
|
|
|
if(length.constructor === Array) {
|
|
this.length = length[0]*length[1];
|
|
this.W = length[0];
|
|
this.H = length[1];
|
|
} else {
|
|
this.length = length;
|
|
this.W = Math.ceil(Math.sqrt(this.length));
|
|
this.H = this.W;
|
|
}
|
|
this.utils = new WebCLGLUtils();
|
|
|
|
this.type = (type != undefined) ? type : 'FLOAT';
|
|
this._supportFormat = this.gl.FLOAT;
|
|
//this._supportFormat = this.gl.UNSIGNED_BYTE;
|
|
|
|
this.offset = (offset != undefined) ? offset : 0;
|
|
this.linear = (linear != undefined && linear == true) ? true : false;
|
|
|
|
this.inData; // enqueueWriteBuffer user data
|
|
|
|
this.mode = (mode != undefined) ? mode : "FRAGMENT"; // "FRAGMENT", "VERTEX", "VERTEX_INDEX", "VERTEX_FROM_KERNEL", "VERTEX_AND_FRAGMENT"
|
|
|
|
// readPixel arrays
|
|
this.outArray4Uint8ArrayX = new Uint8Array((this.W*this.H)*4);
|
|
this.outArray4Uint8ArrayY = new Uint8Array((this.W*this.H)*4);
|
|
this.outArray4Uint8ArrayZ = new Uint8Array((this.W*this.H)*4);
|
|
this.outArray4Uint8ArrayW = new Uint8Array((this.W*this.H)*4);
|
|
/*this.outArray4x4Uint8Array = new Uint8Array((this.W*this.H)*4*4);*/
|
|
|
|
this.Packet4Uint8Array_Float = []; // [this.outArray4Uint8ArrayX]
|
|
this.Float = []; // [unpack(this.outArray4Uint8ArrayX)]
|
|
this.Packet4Uint8Array_Float4 = []; // [this.outArray4Uint8ArrayX, ..Y, ..Z, ..W]
|
|
this.Float4 = []; // [unpack(this.outArray4Uint8ArrayX), unpack(..Y), unpack(..Z), unpack(..W)]
|
|
|
|
|
|
// Create FrameBuffer & RenderBuffer
|
|
this.rBuffer = this.gl.createRenderbuffer();
|
|
this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.rBuffer);
|
|
this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT16, this.W, this.H);
|
|
this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null);
|
|
|
|
this.fBuffer = this.gl.createFramebuffer();
|
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.fBuffer);
|
|
this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, this.rBuffer);
|
|
|
|
if(this.mode == "FRAGMENT" || this.mode == "VERTEX_FROM_KERNEL" || this.mode == "VERTEX_AND_FRAGMENT") {
|
|
// Create WebGLTexture buffer
|
|
this.textureData = this.createWebGLTextureBuffer();
|
|
}
|
|
if(this.mode == "VERTEX" || this.mode == "VERTEX_INDEX" || this.mode == "VERTEX_FROM_KERNEL" || this.mode == "VERTEX_AND_FRAGMENT") {
|
|
// Create WebGL buffer
|
|
this.vertexData0 = this.createWebGLBuffer();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create the WebGLTexture buffer
|
|
* @type Void
|
|
*/
|
|
WebCLGLBufferItem.prototype.createWebGLTextureBuffer = function() {
|
|
this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
|
|
var textureData = this.gl.createTexture();
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, textureData);
|
|
if(this.linear != undefined && this.linear == true) {
|
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W,this.H, 0, this.gl.RGBA, this._supportFormat, null);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR_MIPMAP_NEAREST);
|
|
this.gl.generateMipmap(this.gl.TEXTURE_2D);
|
|
} else {
|
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W,this.H, 0, this.gl.RGBA, this._supportFormat, null);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
|
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
|
}
|
|
|
|
return textureData;
|
|
};
|
|
|
|
/**
|
|
* Create the WebGL buffer
|
|
* @type Void
|
|
*/
|
|
WebCLGLBufferItem.prototype.createWebGLBuffer = function() {
|
|
var vertexData = this.gl.createBuffer();
|
|
|
|
return vertexData;
|
|
};
|
|
|
|
/**
|
|
* Write WebGLTexture buffer
|
|
* @param {Array|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} array
|
|
* @param {Bool} [flip=false]
|
|
* @type Void
|
|
*/
|
|
WebCLGLBufferItem.prototype.writeWebGLTextureBuffer = function(arr, flip) {
|
|
this.inData = arr;
|
|
|
|
if(arr instanceof WebGLTexture) this.textureData = arr;
|
|
else {
|
|
if(flip == false || flip == undefined)
|
|
this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
else
|
|
this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL, true);
|
|
this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
this.gl.bindTexture(this.gl.TEXTURE_2D, this.textureData);
|
|
if(arr instanceof HTMLImageElement) {
|
|
this.inData = this.utils.getUint8ArrayFromHTMLImageElement(arr);
|
|
//texImage2D( target, level, internalformat, format, type, TexImageSource);
|
|
if(this.type == 'FLOAT4') {
|
|
this.gl.texImage2D( this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.FLOAT, arr);
|
|
}/* else if(this.type == 'INT4') {
|
|
this.gl.texImage2D( this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, arr);
|
|
}*/
|
|
} else {
|
|
//console.log("Write arr with length of "+arr.length+" in Buffer "+this.type+" with length of "+this.length+" (W: "+this.W+"; H: "+this.H+")");
|
|
|
|
if(this.type == 'FLOAT4') {
|
|
var arrt = new Float32Array((this.W*this.H)*4);
|
|
for(var n=0; n < arr.length; n++) arrt[n] = arr[n];
|
|
//texImage2D( target, level, internalformat, width, height, border, format, type, pixels);
|
|
if(arr instanceof Uint8Array) {
|
|
this.gl.texImage2D( this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W, this.H, 0, this.gl.RGBA, this.gl.FLOAT, arrt);
|
|
} else if(arr instanceof Float32Array) {
|
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W, this.H, 0, this.gl.RGBA, this.gl.FLOAT, arrt);
|
|
} else {
|
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W, this.H, 0, this.gl.RGBA, this.gl.FLOAT, arrt);
|
|
}
|
|
} else if(this.type == 'FLOAT') {
|
|
var arrayTemp = new Float32Array(this.W*this.H*4);
|
|
|
|
for(var n = 0, f = this.W*this.H; n < f; n++) {
|
|
var idd = n*4;
|
|
arrayTemp[idd] = arr[n];
|
|
arrayTemp[idd+1] = 0.0;
|
|
arrayTemp[idd+2] = 0.0;
|
|
arrayTemp[idd+3] = 0.0;
|
|
}
|
|
arr = arrayTemp;
|
|
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.W, this.H, 0, this.gl.RGBA, this.gl.FLOAT, arr);
|
|
}
|
|
}
|
|
}
|
|
if(this.linear) this.gl.generateMipmap(this.gl.TEXTURE_2D);
|
|
};
|
|
|
|
/**
|
|
* Write WebGL buffer
|
|
* @param {Array|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} array
|
|
* @param {Bool} [flip=false]
|
|
* @type Void
|
|
*/
|
|
WebCLGLBufferItem.prototype.writeWebGLBuffer = function(arr, flip) {
|
|
this.inData = arr;
|
|
if(this.mode == "VERTEX_INDEX") { // "VERTEX_INDEX" ELEMENT_ARRAY_BUFFER
|
|
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexData0);
|
|
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(arr), this.gl.DYNAMIC_DRAW);
|
|
} else { // "VERTEX" || "VERTEX_AND_FRAGMENT" ARRAY_BUFFER
|
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexData0);
|
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(arr), this.gl.DYNAMIC_DRAW);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Remove this buffer
|
|
* @type Void
|
|
*/
|
|
WebCLGLBufferItem.prototype.remove = function() {
|
|
this.gl.deleteRenderbuffer(this.rBuffer);
|
|
this.gl.deleteFramebuffer(this.fBuffer);
|
|
|
|
if(this.mode == "FRAGMENT" || this.mode == "VERTEX_FROM_KERNEL" || this.mode == "VERTEX_AND_FRAGMENT")
|
|
this.gl.deleteTexture(this.textureData);
|
|
|
|
if(this.mode == "VERTEX" || this.mode == "VERTEX_INDEX" || this.mode == "VERTEX_FROM_KERNEL" || this.mode == "VERTEX_AND_FRAGMENT")
|
|
this.gl.deleteBuffer(this.vertexData0);
|
|
};
|
|
/**
|
|
* WebCLGLKernel Object
|
|
* @class
|
|
* @constructor
|
|
*/
|
|
WebCLGLKernel = function(gl, source, header) {
|
|
this.gl = gl;
|
|
var highPrecisionSupport = this.gl.getShaderPrecisionFormat(this.gl.FRAGMENT_SHADER, this.gl.HIGH_FLOAT);
|
|
this.precision = (highPrecisionSupport.precision != 0) ? 'precision highp float;\n\nprecision highp int;\n\n' : 'precision lowp float;\n\nprecision lowp int;\n\n';
|
|
|
|
this.utils = new WebCLGLUtils();
|
|
|
|
this.in_values = [];
|
|
|
|
if(source != undefined) this.setKernelSource(source, header);
|
|
};
|
|
|
|
/**
|
|
* Update the kernel source
|
|
* @type Void
|
|
* @param {String} source
|
|
* @param {String} header Additional functions
|
|
*/
|
|
WebCLGLKernel.prototype.setKernelSource = function(source, header) {
|
|
this.head =(header!=undefined)?header:'';
|
|
this.in_values = [];//{value,type,name,idPointer}
|
|
// value: argument value
|
|
// type: 'buffer_float4'(RGBA channels), 'buffer_float'(Red channel)
|
|
// name: argument name
|
|
// idPointer to: this.samplers or this.uniforms (according to type)
|
|
|
|
var argumentsSource = source.split(')')[0].split('(')[1].split(','); // "float* A", "float* B", "float C", "float4* D"
|
|
//console.log(argumentsSource);
|
|
for(var n = 0, f = argumentsSource.length; n < f; n++) {
|
|
if(argumentsSource[n].match(/\*/gm) != null) {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_values[n] = { value:undefined,
|
|
type:'buffer_float4',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_values[n] = { value:undefined,
|
|
type:'buffer_float',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
}
|
|
} else {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_values[n] = { value:undefined,
|
|
type:'float4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_values[n] = { value:undefined,
|
|
type:'float',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/mat4/gm) != null) {
|
|
this.in_values[n] = { value:undefined,
|
|
type:'mat4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
}
|
|
}
|
|
}
|
|
//console.log(this.in_values);
|
|
|
|
//console.log('original source: '+source);
|
|
this.source = source.replace(/\r\n/gi, '').replace(/\r/gi, '').replace(/\n/gi, '');
|
|
this.source = this.source.replace(/^\w* \w*\([\w\s\*,]*\) {/gi, '').replace(/}(\s|\t)*$/gi, '');
|
|
//console.log('minified source: '+this.source);
|
|
|
|
this.source = this.parse(this.source);
|
|
this.compile();
|
|
};
|
|
/**
|
|
* @private
|
|
*/
|
|
WebCLGLKernel.prototype.parse = function(source) {
|
|
//console.log(source);
|
|
for(var n = 0, f = this.in_values.length; n < f; n++) { // for each in_values (in argument)
|
|
var regexp = new RegExp(this.in_values[n].name+'\\[\\w*\\]',"gm");
|
|
var varMatches = source.match(regexp);// "Search current "in_values.name[xxx]" in source and store in array varMatches
|
|
//console.log(varMatches);
|
|
if(varMatches != null) {
|
|
for(var nB = 0, fB = varMatches.length; nB < fB; nB++) { // for each varMatches ("A[x]", "A[x]")
|
|
var regexpNativeGL = new RegExp('```(\s|\t)*gl.*'+varMatches[nB]+'.*```[^```(\s|\t)*gl]',"gm");
|
|
var regexpNativeGLMatches = source.match(regexpNativeGL);
|
|
if(regexpNativeGLMatches == null) {
|
|
var name = varMatches[nB].split('[')[0];
|
|
var vari = varMatches[nB].split('[')[1].split(']')[0];
|
|
var regexp = new RegExp(name+'\\['+vari.trim()+'\\]',"gm");
|
|
|
|
if(this.in_values[n].type == 'buffer_float4')
|
|
source = source.replace(regexp, 'buffer_float4_data('+name+','+vari+')');
|
|
if(this.in_values[n].type == 'buffer_float')
|
|
source = source.replace(regexp, 'buffer_float_data('+name+','+vari+')');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
source = source.replace(/```(\s|\t)*gl/gi, "").replace(/```/gi, "").replace(/;/gi, ";\n").replace(/}/gi, "}\n").replace(/{/gi, "{\n");
|
|
//console.log('%c translated source:'+source, "background-color:#000;color:#FFF");
|
|
return source;
|
|
};
|
|
/**
|
|
* @private
|
|
*/
|
|
WebCLGLKernel.prototype.compile = function() {
|
|
lines_attrs = (function() {
|
|
str = '';
|
|
for(var n = 0, f = this.in_values.length; n < f; n++) {
|
|
if(this.in_values[n].type == 'buffer_float4' || this.in_values[n].type == 'buffer_float') {
|
|
str += 'uniform sampler2D '+this.in_values[n].name+';\n';
|
|
} else if(this.in_values[n].type == 'float') {
|
|
str += 'uniform float '+this.in_values[n].name+';\n';
|
|
} else if(this.in_values[n].type == 'float4') {
|
|
str += 'uniform vec4 '+this.in_values[n].name+';\n';
|
|
} else if(this.in_values[n].type == 'mat4') {
|
|
str += 'uniform mat4 '+this.in_values[n].name+';\n';
|
|
}
|
|
}
|
|
return str;
|
|
}).bind(this);
|
|
|
|
var sourceVertex = this.precision+
|
|
'attribute vec3 aVertexPosition;\n'+
|
|
'attribute vec2 aTextureCoord;\n'+
|
|
|
|
'varying vec2 global_id;\n'+
|
|
|
|
'void main(void) {\n'+
|
|
'gl_Position = vec4(aVertexPosition, 1.0);\n'+
|
|
'global_id = aTextureCoord;\n'+
|
|
'}\n';
|
|
var sourceFragment = this.precision+
|
|
|
|
lines_attrs()+
|
|
|
|
//this.utils.unpackGLSLFunctionString()+
|
|
|
|
'varying vec2 global_id;\n'+
|
|
'uniform float uBufferWidth;'+
|
|
'uniform float uGeometryLength;'+
|
|
|
|
'vec4 buffer_float4_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor;\n'+
|
|
'}\n'+
|
|
'float buffer_float_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor.x;\n'+
|
|
'}\n'+
|
|
|
|
'vec2 get_global_id() {\n'+
|
|
'return global_id;\n'+
|
|
'}\n'+
|
|
|
|
'vec2 get_global_id(float id) {\n'+
|
|
'float num = (id*uGeometryLength)/uBufferWidth;'+
|
|
'float column = fract(num)*uBufferWidth;'+
|
|
'float row = floor(num);'+
|
|
|
|
'float ts = 1.0/(uBufferWidth-1.0);'+
|
|
'float xx = column*ts;'+
|
|
'float yy = row*ts;'+
|
|
|
|
'return vec2(xx, yy);'+
|
|
'}\n'+
|
|
|
|
this.head+
|
|
|
|
'void main(void) {\n'+
|
|
'float out_float = -999.99989;\n'+
|
|
'vec4 out_float4;\n'+
|
|
|
|
this.source;
|
|
|
|
|
|
|
|
var sourceFrag = sourceFragment+
|
|
'if(out_float != -999.99989) gl_FragColor = vec4(out_float,0.0,0.0,1.0);\n'+
|
|
'else gl_FragColor = out_float4;\n'+
|
|
'}\n';
|
|
|
|
this.kernelPrograms = [ new WebCLGLKernelProgram(this.gl, sourceVertex, sourceFrag, this.in_values) ];
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Bind float or a WebCLGLBuffer to a kernel argument
|
|
* @type Void
|
|
* @param {Int|String} argument Id of argument or name of this
|
|
* @param {Float|Int|Array<Float4>|Array<Mat4>|WebCLGLBuffer} data
|
|
*/
|
|
WebCLGLKernel.prototype.setKernelArg = function(argument, data) {
|
|
if(data == undefined) alert("Error in setKernelArg("+argument+", data) (this data is undefined)");
|
|
|
|
var numArg;
|
|
if(typeof argument != "string") {
|
|
numArg = argument;
|
|
} else {
|
|
for(var n=0, fn = this.in_values.length; n < fn; n++) {
|
|
if(this.in_values[n].name == argument) {
|
|
numArg = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.in_values[numArg] == undefined) {
|
|
console.log("argument "+argument+" not exist in this kernel");
|
|
return;
|
|
}
|
|
this.in_values[numArg].value = data;
|
|
|
|
for(var n=0, fn = this.kernelPrograms.length; n < fn; n++) {
|
|
var kp = this.kernelPrograms[n];
|
|
|
|
if(this.in_values[numArg].type == 'buffer_float4' || this.in_values[numArg].type == 'buffer_float') {
|
|
kp.samplers[this.in_values[numArg].idPointer].value = this.in_values[numArg].value;
|
|
} else if(this.in_values[numArg].type == 'float' || this.in_values[numArg].type == 'float4' || this.in_values[numArg].type == 'mat4') {
|
|
kp.uniforms[this.in_values[numArg].idPointer].value = this.in_values[numArg].value;
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* WebCLGLKernelProgram Object
|
|
* @class
|
|
* @constructor
|
|
*/
|
|
WebCLGLKernelProgram = function(gl, sv, sf, in_values) {
|
|
this.gl = gl;
|
|
this.utils = new WebCLGLUtils();
|
|
|
|
this.kernel = this.gl.createProgram();
|
|
this.utils.createShader(this.gl, "WEBCLGL", sv, sf, this.kernel);
|
|
//console.log(sourceF);
|
|
|
|
this.samplers = []; // {location,value}
|
|
this.uniforms = []; // {location,value}
|
|
this.attr_VertexPos = this.gl.getAttribLocation(this.kernel, "aVertexPosition");
|
|
this.attr_TextureCoord = this.gl.getAttribLocation(this.kernel, "aTextureCoord");
|
|
|
|
this.uBufferWidth = this.gl.getUniformLocation(this.kernel, "uBufferWidth");
|
|
this.uGeometryLength = this.gl.getUniformLocation(this.kernel, "uGeometryLength");
|
|
|
|
for(var n = 0, f = in_values.length; n < f; n++) {
|
|
if(in_values[n].type == 'buffer_float4' || in_values[n].type == 'buffer_float') {
|
|
this.samplers.push({ location: [this.gl.getUniformLocation(this.kernel, in_values[n].name)],
|
|
value:in_values[n].value,
|
|
type: in_values[n].type,
|
|
name: in_values[n].name});
|
|
|
|
in_values[n].idPointer = this.samplers.length-1;
|
|
} else if(in_values[n].type == 'float' || in_values[n].type == 'float4' || in_values[n].type == 'mat4') {
|
|
this.uniforms.push({ location: [this.gl.getUniformLocation(this.kernel, in_values[n].name)],
|
|
value:in_values[n].value,
|
|
type: in_values[n].type,
|
|
name: in_values[n].name});
|
|
|
|
in_values[n].idPointer = this.uniforms.length-1;
|
|
}
|
|
}
|
|
};/**
|
|
* Utilities
|
|
* @class
|
|
* @constructor
|
|
*/
|
|
WebCLGLUtils = function() {
|
|
|
|
};
|
|
|
|
/** @private */
|
|
WebCLGLUtils.prototype.isPowerOfTwo = function(x) {
|
|
return (x & (x - 1)) == 0;
|
|
};
|
|
/** @private */
|
|
WebCLGLUtils.prototype.nextHighestPowerOfTwo = function(x) {
|
|
--x;
|
|
for (var i = 1; i < 32; i <<= 1) {
|
|
x = x | x >> i;
|
|
}
|
|
return x + 1;
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.loadQuad = function(node, length, height) {
|
|
var l=(length==undefined)?0.5:length;
|
|
var h=(height==undefined)?0.5:height;
|
|
this.vertexArray = [-l, -h, 0.0,
|
|
l, -h, 0.0,
|
|
l, h, 0.0,
|
|
-l, h, 0.0];
|
|
|
|
this.textureArray = [0.0, 0.0, 0.0,
|
|
1.0, 0.0, 0.0,
|
|
1.0, 1.0, 0.0,
|
|
0.0, 1.0, 0.0];
|
|
|
|
this.indexArray = [0, 1, 2, 0, 2, 3];
|
|
|
|
var meshObject = new Object;
|
|
meshObject.vertexArray = this.vertexArray;
|
|
meshObject.vertexItemSize = this.vertexItemSize;
|
|
meshObject.vertexNumItems = this.vertexNumItems;
|
|
|
|
meshObject.textureArray = this.textureArray;
|
|
meshObject.textureItemSize = this.textureItemSize;
|
|
meshObject.textureNumItems = this.textureNumItems;
|
|
|
|
meshObject.indexArray = this.indexArray;
|
|
meshObject.indexItemSize = this.indexItemSize;
|
|
meshObject.indexNumItems = this.indexNumItems;
|
|
|
|
return meshObject;
|
|
};
|
|
/** @private **/
|
|
WebCLGLUtils.prototype.getWebGLContextFromCanvas = function(canvas, ctxOpt) {
|
|
var gl;
|
|
try {
|
|
if(ctxOpt == undefined) gl = canvas.getContext("webgl");
|
|
else gl = canvas.getContext("webgl", ctxOpt);
|
|
} catch(e) {
|
|
gl = null;
|
|
}
|
|
if(gl == null) {
|
|
try {
|
|
if(ctxOpt == undefined) gl = canvas.getContext("experimental-webgl");
|
|
else gl = canvas.getContext("experimental-webgl", ctxOpt);
|
|
} catch(e) {
|
|
gl = null;
|
|
}
|
|
}
|
|
if(gl == null) gl = false;
|
|
return gl;
|
|
};
|
|
/**
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.createShader = function(gl, name, sourceVertex, sourceFragment, shaderProgram) {
|
|
var _sv = false, _sf = false;
|
|
|
|
var makeDebug = (function(infoLog, shader) {
|
|
console.log(infoLog);
|
|
|
|
var arrErrors = [];
|
|
var errors = infoLog.split("\n");
|
|
for(var n = 0, f = errors.length; n < f; n++) {
|
|
if(errors[n].match(/^ERROR/gim) != null) {
|
|
var expl = errors[n].split(':');
|
|
var line = parseInt(expl[2]);
|
|
arrErrors.push([line,errors[n]]);
|
|
}
|
|
}
|
|
var sour = gl.getShaderSource(shader).split("\n");
|
|
sour.unshift("");
|
|
for(var n = 0, f = sour.length; n < f; n++) {
|
|
var lineWithError = false;
|
|
var errorStr = '';
|
|
for(var e = 0, fe = arrErrors.length; e < fe; e++) {
|
|
if(n == arrErrors[e][0]) {
|
|
lineWithError = true;
|
|
errorStr = arrErrors[e][1];
|
|
break;
|
|
}
|
|
}
|
|
if(lineWithError == false) {
|
|
console.log("%c"+n+' %c'+sour[n], "color:black", "color:blue");
|
|
} else {
|
|
console.log('%c►►%c'+n+' %c'+sour[n]+'\n%c'+errorStr, "color:red", "color:black", "color:blue", "color:red");
|
|
}
|
|
}
|
|
}).bind(this);
|
|
|
|
|
|
var shaderVertex = gl.createShader(gl.VERTEX_SHADER);
|
|
gl.shaderSource(shaderVertex, sourceVertex);
|
|
gl.compileShader(shaderVertex);
|
|
if (!gl.getShaderParameter(shaderVertex, gl.COMPILE_STATUS)) {
|
|
alert(name+' ERROR (vertex program). See console.');
|
|
|
|
var infoLog = gl.getShaderInfoLog(shaderVertex);
|
|
console.log("%c"+name+' ERROR (vertex program)', "color:red");
|
|
|
|
if(infoLog != undefined)
|
|
makeDebug(infoLog, shaderVertex);
|
|
} else {
|
|
gl.attachShader(shaderProgram, shaderVertex);
|
|
_sv = true;
|
|
}
|
|
|
|
var shaderFragment = gl.createShader(gl.FRAGMENT_SHADER);
|
|
gl.shaderSource(shaderFragment, sourceFragment);
|
|
gl.compileShader(shaderFragment);
|
|
if (!gl.getShaderParameter(shaderFragment, gl.COMPILE_STATUS)) {
|
|
alert(name+' ERROR (fragment program). See console.');
|
|
|
|
var infoLog = gl.getShaderInfoLog(shaderFragment);
|
|
console.log("%c"+name+' ERROR (fragment program)', "color:red");
|
|
|
|
if(infoLog != undefined)
|
|
makeDebug(infoLog, shaderFragment);
|
|
} else {
|
|
gl.attachShader(shaderProgram, shaderFragment);
|
|
_sf = true;
|
|
}
|
|
|
|
if(_sv == true && _sf == true) {
|
|
gl.linkProgram(shaderProgram);
|
|
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
|
alert('Error in shader '+name);
|
|
console.log('Error shader program '+name+':\n ');
|
|
if(gl.getProgramInfoLog(shaderProgram) != undefined) {
|
|
console.log(gl.getProgramInfoLog(shaderProgram));
|
|
}
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Get Uint8Array from HTMLImageElement
|
|
* @returns {Uint8Array}
|
|
* @param {HTMLImageElement} imageElement
|
|
*/
|
|
WebCLGLUtils.prototype.getUint8ArrayFromHTMLImageElement = function(imageElement) {
|
|
var e = document.createElement('canvas');
|
|
e.width = imageElement.width;
|
|
e.height = imageElement.height;
|
|
var ctx2D_tex = e.getContext("2d");
|
|
ctx2D_tex.drawImage(imageElement, 0, 0);
|
|
var arrayTex = ctx2D_tex.getImageData(0, 0, imageElement.width, imageElement.height);
|
|
|
|
return arrayTex.data;
|
|
};
|
|
/**
|
|
* Dot product vector4float
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.dot4 = function(vector4A,vector4B) {
|
|
return vector4A[0]*vector4B[0] + vector4A[1]*vector4B[1] + vector4A[2]*vector4B[2] + vector4A[3]*vector4B[3];
|
|
};
|
|
/**
|
|
* Compute the fractional part of the argument. fract(pi)=0.14159265...
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.fract = function(number) {
|
|
return number - Math.floor(number);
|
|
};
|
|
/**
|
|
* Pack 1float (0.0-1.0) to 4float rgba (0.0-1.0, 0.0-1.0, 0.0-1.0, 0.0-1.0)
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.pack = function(v) {
|
|
var bias = [1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 0.0];
|
|
|
|
var r = v;
|
|
var g = this.fract(r * 255.0);
|
|
var b = this.fract(g * 255.0);
|
|
var a = this.fract(b * 255.0);
|
|
var colour = [r, g, b, a];
|
|
|
|
var dd = [colour[1]*bias[0],colour[2]*bias[1],colour[3]*bias[2],colour[3]*bias[3]];
|
|
|
|
return [colour[0]-dd[0],colour[1]-dd[1],colour[2]-dd[2],colour[3]-dd[3] ];
|
|
};
|
|
/**
|
|
* Unpack 4float rgba (0.0-1.0, 0.0-1.0, 0.0-1.0, 0.0-1.0) to 1float (0.0-1.0)
|
|
* @private
|
|
*/
|
|
WebCLGLUtils.prototype.unpack = function(colour) {
|
|
var bitShifts = [1.0, 1.0/255.0, 1.0/(255.0*255.0), 1.0/(255.0*255.0*255.0)];
|
|
return this.dot4(colour, bitShifts);
|
|
};
|
|
/**
|
|
* Get pack GLSL function string
|
|
* @returns {String}
|
|
*/
|
|
WebCLGLUtils.prototype.packGLSLFunctionString = function() {
|
|
return 'vec4 pack (float depth) {\n'+
|
|
'const vec4 bias = vec4(1.0 / 255.0,\n'+
|
|
'1.0 / 255.0,\n'+
|
|
'1.0 / 255.0,\n'+
|
|
'0.0);\n'+
|
|
|
|
'float r = depth;\n'+
|
|
'float g = fract(r * 255.0);\n'+
|
|
'float b = fract(g * 255.0);\n'+
|
|
'float a = fract(b * 255.0);\n'+
|
|
'vec4 colour = vec4(r, g, b, a);\n'+
|
|
|
|
'return colour - (colour.yzww * bias);\n'+
|
|
'}\n';
|
|
};
|
|
/**
|
|
* Get unpack GLSL function string
|
|
* @returns {String}
|
|
*/
|
|
WebCLGLUtils.prototype.unpackGLSLFunctionString = function() {
|
|
return 'float unpack (vec4 colour) {\n'+
|
|
'const vec4 bitShifts = vec4(1.0,\n'+
|
|
'1.0 / 255.0,\n'+
|
|
'1.0 / (255.0 * 255.0),\n'+
|
|
'1.0 / (255.0 * 255.0 * 255.0));\n'+
|
|
'return dot(colour, bitShifts);\n'+
|
|
'}\n';
|
|
};
|
|
/**
|
|
* WebCLGLVertexFragmentProgram Object
|
|
* @class
|
|
* @constructor
|
|
*/
|
|
WebCLGLVertexFragmentProgram = function(gl, vertexSource, vertexHeader, fragmentSource, fragmentHeader) {
|
|
this.gl = gl;
|
|
var highPrecisionSupport = this.gl.getShaderPrecisionFormat(this.gl.FRAGMENT_SHADER, this.gl.HIGH_FLOAT);
|
|
this.precision = (highPrecisionSupport.precision != 0) ? 'precision highp float;\n\nprecision highp int;\n\n' : 'precision lowp float;\n\nprecision lowp int;\n\n';
|
|
|
|
this.utils = new WebCLGLUtils();
|
|
|
|
this.vertexSource;
|
|
this.fragmentSource;
|
|
this.in_vertex_values = [];
|
|
this.in_fragment_values = [];
|
|
|
|
this.vertexAttributes = []; // {location,value}
|
|
this.vertexUniforms = []; // {location,value}
|
|
this.fragmentSamplers = []; // {location,value}
|
|
this.fragmentUniforms = []; // {location,value}
|
|
|
|
if(vertexSource != undefined) this.setVertexSource(vertexSource, vertexHeader);
|
|
if(fragmentSource != undefined) this.setFragmentSource(fragmentSource, fragmentHeader);
|
|
};
|
|
|
|
/**
|
|
* Update the vertex source
|
|
* @type Void
|
|
* @param {String} vertexSource
|
|
* @param {String} vertexHeader
|
|
*/
|
|
WebCLGLVertexFragmentProgram.prototype.setVertexSource = function(vertexSource, vertexHeader) {
|
|
this.vertexHead =(vertexHeader!=undefined)?vertexHeader:'';
|
|
this.in_vertex_values = [];//{value,type,name,idPointer}
|
|
// value: argument value
|
|
// type: 'buffer_float4_fromKernel'(4 packet pointer4), 'buffer_float_fromKernel'(1 packet pointer4), 'buffer_float4'(1 pointer4), 'buffer_float'(1 pointer1)
|
|
// name: argument name
|
|
// idPointer to: this.vertexAttributes or this.vertexUniforms (according to type)
|
|
|
|
var argumentsSource = vertexSource.split(')')[0].split('(')[1].split(','); // "float* A", "float* B", "float C", "float4* D"
|
|
//console.log(argumentsSource);
|
|
for(var n = 0, f = argumentsSource.length; n < f; n++) {
|
|
if(argumentsSource[n].match(/\*kernel/gm) != null) {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'buffer_float4_fromKernel',
|
|
name:argumentsSource[n].split('*kernel')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'buffer_float_fromKernel',
|
|
name:argumentsSource[n].split('*kernel')[1].trim()};
|
|
}
|
|
} else if(argumentsSource[n].match(/\*/gm) != null) {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'buffer_float4',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'buffer_float',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
}
|
|
} else {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'float4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'float',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/mat4/gm) != null) {
|
|
this.in_vertex_values[n] = { value:undefined,
|
|
type:'mat4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
}
|
|
}
|
|
}
|
|
//console.log(this.in_vertex_values);
|
|
|
|
//console.log('original source: '+vertexSource);
|
|
this.vertexSource = vertexSource.replace(/\r\n/gi, '').replace(/\r/gi, '').replace(/\n/gi, '');
|
|
this.vertexSource = this.vertexSource.replace(/^\w* \w*\([\w\s\*,]*\) {/gi, '').replace(/}(\s|\t)*$/gi, '');
|
|
//console.log('minified source: '+this.vertexSource);
|
|
|
|
this.vertexSource = this.parseVertexSource(this.vertexSource);
|
|
|
|
if(this.fragmentSource != undefined) this.compileVertexFragmentSource();
|
|
};
|
|
/** @private **/
|
|
WebCLGLVertexFragmentProgram.prototype.parseVertexSource = function(source) {
|
|
//console.log(source);
|
|
for(var n = 0, f = this.in_vertex_values.length; n < f; n++) { // for each in_vertex_values (in argument)
|
|
var regexp = new RegExp(this.in_vertex_values[n].name+'\\[\\w*\\]',"gm");
|
|
var varMatches = source.match(regexp);// "Search current "in_vertex_values.name[xxx]" in source and store in array varMatches
|
|
//console.log(varMatches);
|
|
if(varMatches != null) {
|
|
for(var nB = 0, fB = varMatches.length; nB < fB; nB++) { // for each varMatches ("A[x]", "A[x]")
|
|
var regexpNativeGL = new RegExp('```(\s|\t)*gl.*'+varMatches[nB]+'.*```[^```(\s|\t)*gl]',"gm");
|
|
var regexpNativeGLMatches = source.match(regexpNativeGL);
|
|
if(regexpNativeGLMatches == null) {
|
|
var name = varMatches[nB].split('[')[0];
|
|
var vari = varMatches[nB].split('[')[1].split(']')[0];
|
|
var regexp = new RegExp(name+'\\['+vari.trim()+'\\]',"gm");
|
|
|
|
if(this.in_vertex_values[n].type == 'buffer_float4_fromKernel')
|
|
source = source.replace(regexp, 'buffer_float4_fromKernel_data('+name+','+vari+')');
|
|
if(this.in_vertex_values[n].type == 'buffer_float_fromKernel')
|
|
source = source.replace(regexp, 'buffer_float_fromKernel_data('+name+','+vari+')');
|
|
if(this.in_vertex_values[n].type == 'buffer_float4')
|
|
source = source.replace(regexp, name);
|
|
if(this.in_vertex_values[n].type == 'buffer_float')
|
|
source = source.replace(regexp, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
source = source.replace(/```(\s|\t)*gl/gi, "").replace(/```/gi, "").replace(/;/gi, ";\n").replace(/}/gi, "}\n").replace(/{/gi, "{\n");
|
|
//console.log('%c translated source:'+source, "background-color:#000;color:#FFF");
|
|
return source;
|
|
};
|
|
/**
|
|
* Update the fragment source
|
|
* @type Void
|
|
* @param {String} fragmentSource
|
|
* @param {String} fragmentHeader
|
|
*/
|
|
WebCLGLVertexFragmentProgram.prototype.setFragmentSource = function(fragmentSource, fragmentHeader) {
|
|
this.fragmentHead =(fragmentHeader!=undefined)?fragmentHeader:'';
|
|
this.in_fragment_values = [];//{value,type,name,idPointer}
|
|
// value: argument value
|
|
// type: 'buffer_float4'(RGBA channels), 'buffer_float'(Red channel)
|
|
// name: argument name
|
|
// idPointer to: this.fragmentSamplers or this.fragmentUniforms (according to type)
|
|
|
|
var argumentsSource = fragmentSource.split(')')[0].split('(')[1].split(','); // "float* A", "float* B", "float C", "float4* D"
|
|
//console.log(argumentsSource);
|
|
for(var n = 0, f = argumentsSource.length; n < f; n++) {
|
|
if(argumentsSource[n].match(/\*/gm) != null) {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_fragment_values[n] = { value:undefined,
|
|
type:'buffer_float4',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_fragment_values[n] = { value:undefined,
|
|
type:'buffer_float',
|
|
name:argumentsSource[n].split('*')[1].trim()};
|
|
}
|
|
} else {
|
|
if(argumentsSource[n].match(/float4/gm) != null) {
|
|
this.in_fragment_values[n] = { value:undefined,
|
|
type:'float4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/float/gm) != null) {
|
|
this.in_fragment_values[n] = { value:undefined,
|
|
type:'float',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
} else if(argumentsSource[n].match(/mat4/gm) != null) {
|
|
this.in_fragment_values[n] = { value:undefined,
|
|
type:'mat4',
|
|
name:argumentsSource[n].split(' ')[1].trim()};
|
|
}
|
|
}
|
|
}
|
|
//console.log(this.in_fragment_values);
|
|
|
|
//console.log('original source: '+source);
|
|
this.fragmentSource = fragmentSource.replace(/\r\n/gi, '').replace(/\r/gi, '').replace(/\n/gi, '');
|
|
this.fragmentSource = this.fragmentSource.replace(/^\w* \w*\([\w\s\*,]*\) {/gi, '').replace(/}(\s|\t)*$/gi, '');
|
|
//console.log('minified source: '+this.fragmentSource);
|
|
|
|
this.fragmentSource = this.parseFragmentSource(this.fragmentSource);
|
|
|
|
if(this.vertexSource != undefined) this.compileVertexFragmentSource();
|
|
};
|
|
/** @private **/
|
|
WebCLGLVertexFragmentProgram.prototype.parseFragmentSource = function(source) {
|
|
//console.log(source);
|
|
for(var n = 0, f = this.in_fragment_values.length; n < f; n++) { // for each in_fragment_values (in argument)
|
|
var regexp = new RegExp(this.in_fragment_values[n].name+'\\[\\w*\\]',"gm");
|
|
var varMatches = source.match(regexp);// "Search current "in_fragment_values.name[xxx]" in source and store in array varMatches
|
|
//console.log(varMatches);
|
|
if(varMatches != null) {
|
|
for(var nB = 0, fB = varMatches.length; nB < fB; nB++) { // for each varMatches ("A[x]", "A[x]")
|
|
var regexpNativeGL = new RegExp('```(\s|\t)*gl.*'+varMatches[nB]+'.*```[^```(\s|\t)*gl]',"gm");
|
|
var regexpNativeGLMatches = source.match(regexpNativeGL);
|
|
if(regexpNativeGLMatches == null) {
|
|
var name = varMatches[nB].split('[')[0];
|
|
var vari = varMatches[nB].split('[')[1].split(']')[0];
|
|
var regexp = new RegExp(name+'\\['+vari.trim()+'\\]',"gm");
|
|
|
|
if(this.in_fragment_values[n].type == 'buffer_float4')
|
|
source = source.replace(regexp, 'buffer_float4_data('+name+','+vari+')');
|
|
if(this.in_fragment_values[n].type == 'buffer_float')
|
|
source = source.replace(regexp, 'buffer_float_data('+name+','+vari+')');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
source = source.replace(/```(\s|\t)*gl/gi, "").replace(/```/gi, "").replace(/;/gi, ";\n").replace(/}/gi, "}\n").replace(/{/gi, "{\n");
|
|
//console.log('%c translated source:'+source, "background-color:#000;color:#FFF");
|
|
return source;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @private **/
|
|
WebCLGLVertexFragmentProgram.prototype.compileVertexFragmentSource = function() {
|
|
lines_vertex_attrs = (function() {
|
|
str = '';
|
|
for(var n = 0, f = this.in_vertex_values.length; n < f; n++) {
|
|
if(this.in_vertex_values[n].type == 'buffer_float4_fromKernel' || this.in_vertex_values[n].type == 'buffer_float_fromKernel') {
|
|
str += 'uniform sampler2D '+this.in_vertex_values[n].name+';\n';
|
|
} else if(this.in_vertex_values[n].type == 'buffer_float4') {
|
|
str += 'attribute vec4 '+this.in_vertex_values[n].name+';\n';
|
|
} else if(this.in_vertex_values[n].type == 'buffer_float') {
|
|
str += 'attribute float '+this.in_vertex_values[n].name+';\n';
|
|
} else if(this.in_vertex_values[n].type == 'float') {
|
|
str += 'uniform float '+this.in_vertex_values[n].name+';\n';
|
|
} else if(this.in_vertex_values[n].type == 'float4') {
|
|
str += 'uniform vec4 '+this.in_vertex_values[n].name+';\n';
|
|
} else if(this.in_vertex_values[n].type == 'mat4') {
|
|
str += 'uniform mat4 '+this.in_vertex_values[n].name+';\n';
|
|
}
|
|
}
|
|
return str;
|
|
}).bind(this);
|
|
|
|
lines_fragment_attrs = (function() {
|
|
str = '';
|
|
for(var n = 0, f = this.in_fragment_values.length; n < f; n++) {
|
|
if(this.in_fragment_values[n].type == 'buffer_float4' || this.in_fragment_values[n].type == 'buffer_float') {
|
|
str += 'uniform sampler2D '+this.in_fragment_values[n].name+';\n';
|
|
} else if(this.in_fragment_values[n].type == 'float') {
|
|
str += 'uniform float '+this.in_fragment_values[n].name+';\n';
|
|
} else if(this.in_fragment_values[n].type == 'float4') {
|
|
str += 'uniform vec4 '+this.in_fragment_values[n].name+';\n';
|
|
} else if(this.in_fragment_values[n].type == 'mat4') {
|
|
str += 'uniform mat4 '+this.in_fragment_values[n].name+';\n';
|
|
}
|
|
}
|
|
return str;
|
|
}).bind(this);
|
|
|
|
|
|
var sourceVertex = this.precision+
|
|
'uniform float uOffset;\n'+
|
|
'uniform float uBufferWidth;'+
|
|
'uniform float uGeometryLength;'+
|
|
|
|
lines_vertex_attrs()+
|
|
|
|
this.utils.unpackGLSLFunctionString()+
|
|
|
|
'vec4 buffer_float4_fromKernel_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor;\n'+
|
|
'}\n'+
|
|
'float buffer_float_fromKernel_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor.x;\n'+
|
|
'}\n'+
|
|
|
|
'vec2 get_global_id() {\n'+
|
|
'return vec2(0.0, 0.0);\n'+
|
|
'}\n'+
|
|
|
|
'vec2 get_global_id(float id) {\n'+
|
|
'float num = (id*uGeometryLength)/uBufferWidth;'+
|
|
'float column = fract(num)*uBufferWidth;'+
|
|
'float row = floor(num);'+
|
|
|
|
'float ts = 1.0/(uBufferWidth-1.0);'+
|
|
'float xx = column*ts;'+
|
|
'float yy = row*ts;'+
|
|
|
|
'return vec2(xx, yy);'+
|
|
'}\n'+
|
|
|
|
this.vertexHead+
|
|
|
|
'void main(void) {\n'+
|
|
|
|
this.vertexSource+
|
|
|
|
'}\n';
|
|
//console.log(sourceVertex);
|
|
var sourceFragment = this.precision+
|
|
|
|
lines_fragment_attrs()+
|
|
|
|
'vec4 buffer_float4_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor;\n'+
|
|
'}\n'+
|
|
'float buffer_float_data(sampler2D arg, vec2 coord) {\n'+
|
|
'vec4 textureColor = texture2D(arg, coord);\n'+
|
|
'return textureColor.x;\n'+
|
|
'}\n'+
|
|
|
|
'vec2 get_global_id() {\n'+
|
|
'return vec2(0.0, 0.0);\n'+
|
|
'}\n'+
|
|
|
|
this.fragmentHead+
|
|
|
|
'void main(void) {\n'+
|
|
|
|
this.fragmentSource+
|
|
|
|
'}\n';
|
|
//console.log(sourceFragment);
|
|
|
|
this.vertexFragmentProgram = this.gl.createProgram();
|
|
this.utils.createShader(this.gl, "WEBCLGL VERTEX FRAGMENT PROGRAM", sourceVertex, sourceFragment, this.vertexFragmentProgram);
|
|
|
|
|
|
this.vertexAttributes = []; // {location,value}
|
|
this.vertexUniforms = []; // {location,value}
|
|
this.fragmentSamplers = []; // {location,value}
|
|
this.fragmentUniforms = []; // {location,value}
|
|
|
|
this.uOffset = this.gl.getUniformLocation(this.vertexFragmentProgram, "uOffset");
|
|
this.uBufferWidth = this.gl.getUniformLocation(this.vertexFragmentProgram, "uBufferWidth");
|
|
this.uGeometryLength = this.gl.getUniformLocation(this.vertexFragmentProgram, "uGeometryLength");
|
|
|
|
// vertexAttributes & vertexUniforms
|
|
for(var n = 0, f = this.in_vertex_values.length; n < f; n++) {
|
|
if(this.in_vertex_values[n].type == 'buffer_float_fromKernel' || this.in_vertex_values[n].type == 'buffer_float4_fromKernel') {
|
|
this.vertexAttributes.push({ location: [this.gl.getUniformLocation(this.vertexFragmentProgram, this.in_vertex_values[n].name)],
|
|
value: this.in_vertex_values[n].value,
|
|
type: this.in_vertex_values[n].type,
|
|
name: this.in_vertex_values[n].name});
|
|
|
|
this.in_vertex_values[n].idPointer = this.vertexAttributes.length-1;
|
|
} else if(this.in_vertex_values[n].type == 'buffer_float4' || this.in_vertex_values[n].type == 'buffer_float') {
|
|
this.vertexAttributes.push({ location: [this.gl.getAttribLocation(this.vertexFragmentProgram, this.in_vertex_values[n].name)],
|
|
value: this.in_vertex_values[n].value,
|
|
type: this.in_vertex_values[n].type,
|
|
name: this.in_vertex_values[n].name});
|
|
|
|
this.in_vertex_values[n].idPointer = this.vertexAttributes.length-1;
|
|
} else if(this.in_vertex_values[n].type == 'float' || this.in_vertex_values[n].type == 'float4' || this.in_vertex_values[n].type == 'mat4') {
|
|
this.vertexUniforms.push({ location: [this.gl.getUniformLocation(this.vertexFragmentProgram, this.in_vertex_values[n].name)],
|
|
value: this.in_vertex_values[n].value,
|
|
type: this.in_vertex_values[n].type,
|
|
name: this.in_vertex_values[n].name});
|
|
|
|
this.in_vertex_values[n].idPointer = this.vertexUniforms.length-1;
|
|
}
|
|
}
|
|
|
|
// fragmentSamplers & fragmentUniforms
|
|
for(var n = 0, f = this.in_fragment_values.length; n < f; n++) {
|
|
if(this.in_fragment_values[n].type == 'buffer_float4' || this.in_fragment_values[n].type == 'buffer_float') {
|
|
this.fragmentSamplers.push({ location: [this.gl.getUniformLocation(this.vertexFragmentProgram, this.in_fragment_values[n].name)],
|
|
value: this.in_fragment_values[n].value,
|
|
type: this.in_fragment_values[n].type,
|
|
name: this.in_fragment_values[n].name});
|
|
|
|
this.in_fragment_values[n].idPointer = this.fragmentSamplers.length-1;
|
|
} else if(this.in_fragment_values[n].type == 'float' || this.in_fragment_values[n].type == 'float4' || this.in_fragment_values[n].type == 'mat4') {
|
|
this.fragmentUniforms.push({ location: [this.gl.getUniformLocation(this.vertexFragmentProgram, this.in_fragment_values[n].name)],
|
|
value: this.in_fragment_values[n].value,
|
|
type: this.in_fragment_values[n].type,
|
|
name: this.in_fragment_values[n].name});
|
|
|
|
this.in_fragment_values[n].idPointer = this.fragmentUniforms.length-1;
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Bind float, mat4 or a WebCLGLBuffer to a vertex argument
|
|
* @param {Int|String} argument Id of argument or name of this
|
|
* @param {Float|Int|Array<Float4>|Array<Mat4>|WebCLGLBuffer} data
|
|
*/
|
|
WebCLGLVertexFragmentProgram.prototype.setVertexArg = function(argument, data) {
|
|
if(data == undefined) alert("Error: setVertexArg("+argument+", undefined)");
|
|
|
|
var numArg;
|
|
if(typeof argument != "string") {
|
|
numArg = argument;
|
|
} else {
|
|
for(var n=0, fn = this.in_vertex_values.length; n < fn; n++) {
|
|
if(this.in_vertex_values[n].name == argument) {
|
|
numArg = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.in_vertex_values[numArg] == undefined) {
|
|
console.log("argument "+argument+" not exist in this vertex program");
|
|
return;
|
|
}
|
|
this.in_vertex_values[numArg].value = data;
|
|
|
|
if( this.in_vertex_values[numArg].type == 'buffer_float4_fromKernel' ||
|
|
this.in_vertex_values[numArg].type == 'buffer_float_fromKernel' ||
|
|
this.in_vertex_values[numArg].type == 'buffer_float4' ||
|
|
this.in_vertex_values[numArg].type == 'buffer_float') {
|
|
this.vertexAttributes[this.in_vertex_values[numArg].idPointer].value = this.in_vertex_values[numArg].value;
|
|
} else if(this.in_vertex_values[numArg].type == 'float' || this.in_vertex_values[numArg].type == 'float4' || this.in_vertex_values[numArg].type == 'mat4') {
|
|
this.vertexUniforms[this.in_vertex_values[numArg].idPointer].value = this.in_vertex_values[numArg].value;
|
|
}
|
|
};
|
|
/**
|
|
* Bind float or a WebCLGLBuffer to a fragment argument
|
|
* @param {Int|String} argument Id of argument or name of this
|
|
* @param {Float|Int|Array<Float4>|Array<Mat4>|WebCLGLBuffer} data
|
|
*/
|
|
WebCLGLVertexFragmentProgram.prototype.setFragmentArg = function(argument, data) {
|
|
if(data == undefined) alert("Error: setFragmentArg("+argument+", undefined)");
|
|
|
|
var numArg;
|
|
if(typeof argument != "string") {
|
|
numArg = argument;
|
|
} else {
|
|
for(var n=0, fn = this.in_fragment_values.length; n < fn; n++) {
|
|
if(this.in_fragment_values[n].name == argument) {
|
|
numArg = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.in_fragment_values[numArg] == undefined) {
|
|
console.log("argument "+argument+" not exist in this fragment program");
|
|
return;
|
|
}
|
|
this.in_fragment_values[numArg].value = data;
|
|
|
|
if(this.in_fragment_values[numArg].type == 'buffer_float4' || this.in_fragment_values[numArg].type == 'buffer_float') {
|
|
this.fragmentSamplers[this.in_fragment_values[numArg].idPointer].value = this.in_fragment_values[numArg].value;
|
|
} else if(this.in_fragment_values[numArg].type == 'float' || this.in_fragment_values[numArg].type == 'float4' || this.in_fragment_values[numArg].type == 'mat4') {
|
|
this.fragmentUniforms[this.in_fragment_values[numArg].idPointer].value = this.in_fragment_values[numArg].value;
|
|
}
|
|
};
|
|
/**
|
|
* WebCLGLWork Object
|
|
* @class
|
|
* @constructor
|
|
*/
|
|
WebCLGLWork = function(webCLGL, offset) {
|
|
this.webCLGL = webCLGL;
|
|
this.offset = (offset != undefined) ? offset : 100.0;
|
|
|
|
this.kernels = {};
|
|
this.vertexFragmentPrograms = {};
|
|
this.buffers = {};
|
|
this.buffers_TEMP = {};
|
|
|
|
|
|
var kernelPr;
|
|
var vPr;
|
|
var fPr;
|
|
var updatedFromKernel;
|
|
var type; // FLOAT or FLOAT4
|
|
var isBuffer;
|
|
var usedInVertex;
|
|
var usedInFragment;
|
|
var mode; // "FRAGMENT", "VERTEX", "VERTEX_INDEX", "VERTEX_FROM_KERNEL", "VERTEX_AND_FRAGMENT"
|
|
};
|
|
|
|
/**
|
|
* Add one WebCLGLKernel to the work
|
|
* @param {WebCLGLKernel} kernel
|
|
* @param {String} name Name for identify this kernel
|
|
*/
|
|
WebCLGLWork.prototype.addKernel = function(kernel, name) {
|
|
var exists = false;
|
|
for(var key in this.kernels) {
|
|
if(this.kernels[key] == kernel) {
|
|
this.kernels[key] = kernel;
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if(exists == false) {
|
|
this.kernels[name] = kernel;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get one added WebCLGLKernel
|
|
* @param {String} name Get assigned kernel for this argument
|
|
*/
|
|
WebCLGLWork.prototype.getKernel = function(name) {
|
|
for(var key in this.kernels) {
|
|
if(key == name) {
|
|
return this.kernels[key];
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Add one WebCLGLVertexFragmentProgram to the work
|
|
* @param {WebCLGLVertexFragmentProgram} vertexFragmentProgram
|
|
* @param {String} name Name for identify this vertexFragmentProgram
|
|
*/
|
|
WebCLGLWork.prototype.addVertexFragmentProgram = function(vertexFragmentProgram, name) {
|
|
var exists = false;
|
|
for(var key in this.vertexFragmentPrograms) {
|
|
if(this.vertexFragmentPrograms[key] == vertexFragmentProgram) {
|
|
this.vertexFragmentPrograms[key] = vertexFragmentProgram;
|
|
exists = true;
|
|
break;
|
|
}
|
|
}
|
|
if(exists == false) {
|
|
this.vertexFragmentPrograms[name] = vertexFragmentProgram;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
WebCLGLWork.prototype.checkArg = function(argument) {
|
|
kernelPr = [];
|
|
vPr = [];
|
|
fPr = [];
|
|
updatedFromKernel = false;
|
|
isBuffer = false;
|
|
usedInVertex = false;
|
|
usedInFragment = false;
|
|
|
|
for(var key in this.kernels) {
|
|
for(var nb=0; nb < this.kernels[key].in_values.length; nb++) {
|
|
var inValues = this.kernels[key].in_values[nb];
|
|
if(inValues.name == argument) {
|
|
if(inValues.type == "buffer_float4") {
|
|
type = "FLOAT4";
|
|
isBuffer = true;
|
|
} else if(inValues.type == "buffer_float") {
|
|
type = "FLOAT";
|
|
isBuffer = true;
|
|
}
|
|
|
|
kernelPr.push(this.kernels[key]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(updatedFromKernel == false)
|
|
updatedFromKernel = true;
|
|
|
|
}
|
|
|
|
|
|
for(var key in this.vertexFragmentPrograms) {
|
|
for(var nb=0; nb < this.vertexFragmentPrograms[key].in_vertex_values.length; nb++) {
|
|
var inValues = this.vertexFragmentPrograms[key].in_vertex_values[nb];
|
|
if(inValues.name == argument) {
|
|
if(inValues.type == "buffer_float4_fromKernel" || inValues.type == "buffer_float4") {
|
|
type = "FLOAT4";
|
|
isBuffer = true;
|
|
} else if(inValues.type == "buffer_float_fromKernel" || inValues.type == "buffer_float") {
|
|
type = "FLOAT";
|
|
isBuffer = true;
|
|
}
|
|
|
|
vPr.push(this.vertexFragmentPrograms[key]);
|
|
usedInVertex = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(var nb=0; nb < this.vertexFragmentPrograms[key].in_fragment_values.length; nb++) {
|
|
var inValues = this.vertexFragmentPrograms[key].in_fragment_values[nb];
|
|
if(inValues.name == argument) {
|
|
if(inValues.type == "buffer_float4") {
|
|
type = "FLOAT4";
|
|
isBuffer = true;
|
|
} else if(inValues.type == "buffer_float") {
|
|
type = "FLOAT";
|
|
isBuffer = true;
|
|
}
|
|
|
|
fPr.push(this.vertexFragmentPrograms[key]);
|
|
usedInFragment = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Assign value of a argument for all added Kernels and vertexFragmentPrograms
|
|
* @param {String} argument Argument to set
|
|
* @param {Array<Float>|Float32Array|Uint8Array|WebGLTexture|HTMLImageElement} value
|
|
* @param {Array<Float>} [splits=[value.length]]
|
|
* @param {Array<Float2>} [overrideDimensions=new Array(){Math.sqrt(value.length), Math.sqrt(value.length)}]
|
|
*/
|
|
WebCLGLWork.prototype.setArg = function(argument, value, splits, overrideDimensions) {
|
|
this.checkArg(argument);
|
|
|
|
|
|
if(isBuffer == true) {
|
|
if(updatedFromKernel == true && usedInVertex == true) {
|
|
mode = "VERTEX_FROM_KERNEL";
|
|
} else if(usedInVertex == true) {
|
|
if(kernelPr.length > 0 || usedInFragment == true) {
|
|
mode = "VERTEX_AND_FRAGMENT";
|
|
} else {
|
|
mode = "VERTEX";
|
|
}
|
|
} else {
|
|
mode = "FRAGMENT";
|
|
}
|
|
|
|
var length;
|
|
if(overrideDimensions == undefined) {
|
|
length = (value instanceof HTMLImageElement) ? (value.width*value.height) : ((type == "FLOAT4") ? value.length/4 : value.length);
|
|
} else {
|
|
length = [overrideDimensions[0], overrideDimensions[1]];
|
|
}
|
|
var spl = (splits != undefined) ? splits : [length];
|
|
|
|
buff = this.webCLGL.createBuffer(length, type, this.offset, false, mode, spl);
|
|
this.webCLGL.enqueueWriteBuffer(buff, value);
|
|
this.buffers[argument] = buff;
|
|
//if(updatedFromKernel == true) {
|
|
buffTMP = this.webCLGL.createBuffer(length, type, this.offset, false, mode, spl);
|
|
this.webCLGL.enqueueWriteBuffer(buffTMP, value);
|
|
this.buffers_TEMP[argument] = buffTMP;
|
|
//}
|
|
|
|
|
|
for(var n=0; n < kernelPr.length; n++)
|
|
kernelPr[n].setKernelArg(argument, this.buffers[argument]);
|
|
|
|
for(var n=0; n < vPr.length; n++)
|
|
vPr[n].setVertexArg(argument, this.buffers[argument]);
|
|
|
|
for(var n=0; n < fPr.length; n++)
|
|
fPr[n].setFragmentArg(argument, this.buffers[argument]);
|
|
} else {
|
|
for(var n=0; n < kernelPr.length; n++)
|
|
kernelPr[n].setKernelArg(argument, value);
|
|
|
|
for(var n=0; n < vPr.length; n++)
|
|
vPr[n].setVertexArg(argument, value);
|
|
|
|
for(var n=0; n < fPr.length; n++)
|
|
fPr[n].setFragmentArg(argument, value);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set shared argument from other work
|
|
* @param {String} argument Argument to set
|
|
* @param {WebCLGLWork} clglWork
|
|
*/
|
|
WebCLGLWork.prototype.setSharedBufferArg = function(argument, clglWork) {
|
|
this.checkArg(argument);
|
|
|
|
|
|
this.buffers[argument] = clglWork.buffers[argument];
|
|
this.buffers_TEMP[argument] = clglWork.buffers_TEMP[argument];
|
|
|
|
for(var n=0; n < kernelPr.length; n++)
|
|
kernelPr[n].setKernelArg(argument, this.buffers[argument]);
|
|
|
|
for(var n=0; n < vPr.length; n++)
|
|
vPr[n].setVertexArg(argument, this.buffers[argument]);
|
|
|
|
for(var n=0; n < fPr.length; n++)
|
|
fPr[n].setFragmentArg(argument, this.buffers[argument]);
|
|
};
|
|
|
|
/**
|
|
* Get all arguments existing in passed kernels & vertexFragmentPrograms
|
|
* @returns {Object}
|
|
*/
|
|
WebCLGLWork.prototype.getAllArgs = function() {
|
|
var args = {};
|
|
for(var key in this.kernels) {
|
|
for(var nb=0; nb < this.kernels[key].in_values.length; nb++) {
|
|
var inValues = this.kernels[key].in_values[nb];
|
|
args[inValues.name] = inValues;
|
|
}
|
|
}
|
|
|
|
|
|
for(var key in this.vertexFragmentPrograms) {
|
|
for(var nb=0; nb < this.vertexFragmentPrograms[key].in_vertex_values.length; nb++) {
|
|
var inValues = this.vertexFragmentPrograms[key].in_vertex_values[nb];
|
|
args[inValues.name] = inValues;
|
|
}
|
|
|
|
for(var nb=0; nb < this.vertexFragmentPrograms[key].in_fragment_values.length; nb++) {
|
|
var inValues = this.vertexFragmentPrograms[key].in_fragment_values[nb];
|
|
args[inValues.name] = inValues;
|
|
}
|
|
}
|
|
|
|
return args;
|
|
};
|
|
|
|
/**
|
|
* Set indices for the geometry passed in vertexFragmentProgram
|
|
* @param {Array<Float>} array
|
|
* @param {Array<Float>} [splits=[array.length]]
|
|
*/
|
|
WebCLGLWork.prototype.setIndices = function(arr, splits) {
|
|
var spl = (splits != undefined) ? splits : [arr.length];
|
|
this.CLGL_bufferIndices = this.webCLGL.createBuffer(arr.length, "FLOAT", this.offset, false, "VERTEX_INDEX", spl);
|
|
this.webCLGL.enqueueWriteBuffer(this.CLGL_bufferIndices, arr);
|
|
};
|
|
|
|
/**
|
|
* Process kernels
|
|
* @param {String} kernelName
|
|
* @param {WebCLGLBuffer} [webCLGLBuffer=undefined]
|
|
* @param {Int} [geometryLength=1] - Length of geometry (1 for points, 3 for triangles...)
|
|
*/
|
|
WebCLGLWork.prototype.enqueueNDRangeKernel = function(kernelName, argumentToUpdate, geometryLength) {
|
|
this.webCLGL.enqueueNDRangeKernel(this.kernels[kernelName], argumentToUpdate, geometryLength);
|
|
};
|
|
|
|
/**
|
|
* Process VertexFragmentProgram
|
|
* @param {String} [argument=undefined] Argument for vertices count or undefined if indices exist
|
|
* @param {String} Name (vertexFragmentProgramName) of vertexFragmentProgram to execute
|
|
* @param {Int} drawMode
|
|
* @param {Int} [geometryLength=1] - Length of geometry (1 for points, 3 for triangles...)
|
|
*/
|
|
WebCLGLWork.prototype.enqueueVertexFragmentProgram = function(argument, vertexFragmentProgramName, drawMode, geometryLength) {
|
|
var buff = (this.CLGL_bufferIndices != undefined) ? this.CLGL_bufferIndices : this.buffers[argument];
|
|
this.webCLGL.enqueueVertexFragmentProgram(this.vertexFragmentPrograms[vertexFragmentProgramName], buff, drawMode, geometryLength);
|
|
};
|