UNPKG

node-webcl

Version:

A WebCL implementation for desktops with NodeJS

373 lines (306 loc) 11.1 kB
// Copyright (c) 2011-2012, Motorola Mobility, Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Motorola Mobility, Inc. nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var nodejs = (typeof window === 'undefined'); if(nodejs) { webcl = require('../webcl'); clu = require('../lib/clUtils'); util = require('util'); fs = require('fs'); WebGL = require('node-webgl'); document = WebGL.document(); log = console.log; alert = console.log; //Read and eval library for mat/vec operations eval(fs.readFileSync(__dirname + '/glMatrix-0.9.5.min.js', 'utf8')); } else webcl = window.webcl; requestAnimationFrame = document.requestAnimationFrame; //First check if the webcl extension is installed at all if (webcl == undefined) { alert("Unfortunately your system does not support webcl. " + "Make sure that you have the webcl extension installed."); process.exit(-1); } //Rendering window vars var window_width = 512; var window_height = 512; var mesh_width = 128; var mesh_height = 128; //OpenCL vars var cpPlatform; var cxGPUContext; var cdDevices; var cqCommandQueue; var ckKernel; var vbo_cl; var cpProgram; var szGlobalWorkSize = [ mesh_width, mesh_height ]; //vbo variables var vbo; var gl; var mvMatrix = mat4.create(); var pMatrix = mat4.create(); var shaderProgram; //mouse controls var mouse_old_x, mouse_old_y; var mouse_buttons = 0; var rotate_x = 0.0, rotate_y = 0.0; var translate_z = -3.0; //Sim and Auto-Verification parameters var anim = 0.0; var iFrameCount = 0; // FPS count for averaging var iFrameTrigger = 90; // FPS trigger for sampling var iFramesPerSec = 0; // frames per second var iTestSets = 3; var g_Index = 0; document.setTitle("sineGL"); requestAnimFrame = document.requestAnimationFrame; document.on("mousedown", function(evt) { mouse(evt, true); }); document.on("mouseup", function(evt) { mouse(evt, false); }); document.on("mousemove", motion); document.on("resize",function(evt){ console.log('resize to: ('+evt.width+", "+evt.height+")"); gl.viewportWidth=evt.width; gl.viewportHeight=evt.height; gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); }); main(); function main() { log('Init GL'); initGL(); // Pick platform var platformList = webcl.getPlatforms(); var platform = platformList[0]; var devices = platform.getDevices(webcl.DEVICE_TYPE_GPU); device=devices[0]; // make sure we use a discrete GPU for(var i=0;i<devices.length;i++) { var vendor=devices[i].getInfo(webcl.DEVICE_VENDOR); // log('found vendor '+vendor+', is Intel? '+(vendor.indexOf('Intel')>=0)) if(vendor.indexOf('Intel')==-1) device=devices[i]; } log('using device: '+device.getInfo(webcl.DEVICE_VENDOR).trim()+' '+device.getInfo(webcl.DEVICE_NAME)); if(!device.enableExtension('KHR_gl_sharing')) throw new Error("Can NOT use GL sharing"); cxGPUContext = webcl.createContext(gl, device); // create a command-queue cqCommandQueue = cxGPUContext.createCommandQueue(device, 0); // create the program var sourceCL = fs.readFileSync(__dirname + '/sine.cl', 'ascii'); cpProgram = cxGPUContext.createProgram(sourceCL); // build the program try { cpProgram.build(device, "-cl-fast-relaxed-math"); } catch (err) { log('Error building program: ' + err); } log("Build Status: " + cpProgram.getBuildInfo(device, webcl.PROGRAM_BUILD_STATUS)); log("Build Options: " + cpProgram.getBuildInfo(device, webcl.PROGRAM_BUILD_OPTIONS)); log("Build Log: " + cpProgram.getBuildInfo(device, webcl.PROGRAM_BUILD_LOG)); // create the kernel try { ckKernel = cpProgram.createKernel("sine_wave"); } catch (err) { log("Cannot create Kernel: " + err); return; } // create VBO (if using standard GL or CL-GL interop) createVBO(); // set the args values ckKernel.setArg(0, vbo_cl); // way 1 // ckKernel.setArg(1, new Int32Array([mesh_width])); // ckKernel.setArg(2, new Int32Array([mesh_height])); // way 2 var aints=new Int32Array([mesh_width,mesh_height]); var aints2=aints.subarray(1); ckKernel.setArg(1, aints); ckKernel.setArg(2, aints.subarray(1)); // run OpenCL kernel once to generate vertex positions runKernel(0); // start main rendering loop DisplayGL(); } function getShader(gl, id) { var shaders = { "shader-fs" : [ "varying vec4 vColor;", "void main(void) {", " gl_FragColor = vColor;", "}" ].join("\n"), "shader-vs" : [ "attribute vec3 aVertexPosition;", "attribute vec4 aVertexColor;", "uniform mat4 uMVMatrix;", "uniform mat4 uPMatrix;", "varying vec4 vColor;", "void main(void) {", " gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);", " vColor = vec4(1,0,0,1);//aVertexColor;", "}" ].join("\n") }; var shader; if (!shaders.hasOwnProperty(id)) return null; var str = shaders[id]; if (id.match(/-fs/)) { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (id.match(/-vs/)) { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, str); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; } function initShaders() { var fragmentShader = getShader(gl, "shader-fs"); var vertexShader = getShader(gl, "shader-vs"); shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Could not initialise shaders"); } gl.useProgram(shaderProgram); shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor"); gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute); shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); } function initGL() { var canvas = document.createElement("mycanvas", window_width, window_height); try { gl = canvas.getContext("experimental-webgl"); gl.viewportWidth = canvas.width; gl.viewportHeight = canvas.height; } catch (e) { } if (!gl) { alert("Could not initialise WebGL, sorry :-("); } // init shaders initShaders(); // default initialization gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.disable(gl.DEPTH_TEST); // viewport gl.viewport(0, 0, canvas.width, canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // projection mat4.perspective(60, window_width / window_height, 0.1, 10, pMatrix); // set view matrix mat4.identity(mvMatrix); mat4.translate(mvMatrix, [ 0.0, 0.0, translate_z ]); mat4.rotate(mvMatrix, rotate_x * Math.PI / 180, [ 1.0, 0.0, 0.0 ]); mat4.rotate(mvMatrix, rotate_y * Math.PI / 180, [ 0.0, 1.0, 0.0 ]); } function runKernel(time) { // map OpenGL buffer object for writing from OpenCL gl.finish(); cqCommandQueue.enqueueAcquireGLObjects(vbo_cl); // Set arg 3 and execute the kernel ckKernel.setArg(3, new Float32Array([time])); cqCommandQueue.enqueueNDRangeKernel(ckKernel, 2, null, szGlobalWorkSize, null); // unmap buffer object cqCommandQueue.enqueueReleaseGLObjects(vbo_cl); cqCommandQueue.finish(); } function createVBO() { // create VBO var size = mesh_width * mesh_height * 4 * 4; // create buffer object vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // initialize buffer object gl.bufferData(gl.ARRAY_BUFFER, size, gl.DYNAMIC_DRAW); // create OpenCL buffer from GL VBO vbo_cl = cxGPUContext.createFromGLBuffer(webcl.MEM_WRITE_ONLY, vbo); } function setMatrixUniforms() { gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); } function DisplayGL(time) { // increment the geometry computation parameter (or set to reference for Q/A check) anim += 0.01; // run OpenCL kernel to generate vertex positions runKernel(anim); // clear graphics then render from the vbo gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, 4, gl.FLOAT, false, 0, 0); setMatrixUniforms(); gl.drawArrays(gl.POINTS, 0, mesh_width * mesh_height); requestAnimationFrame(DisplayGL); } function mouse(evt, isDown) { //log('mouse event: button=' + evt.button); if (isDown) mouse_buttons |= 1 << evt.button; else mouse_buttons = 0; mouse_old_x = evt.x; mouse_old_y = evt.y; } function motion(evt) { var dx = (evt.x - mouse_old_x); var dy = (evt.y - mouse_old_y); //log('mouse motion: dx=' + dx + " dy=" + dy+ "button="+mouse_buttons); if (mouse_buttons & 1) { rotate_x += dy * 0.2; rotate_y += dx * 0.2; } else if (mouse_buttons & 2) { translate_z += dy * 0.01; } mouse_old_x = evt.x; mouse_old_y = evt.y; // set view matrix //gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); mat4.identity(mvMatrix); mat4.translate(mvMatrix, [ 0.0, 0.0, translate_z ]); mat4.rotate(mvMatrix, rotate_x * Math.PI / 180, [ 1.0, 0.0, 0.0 ]); mat4.rotate(mvMatrix, rotate_y * Math.PI / 180, [ 0.0, 1.0, 0.0 ]); }