Skip to content

Commit

Permalink
update documentation to be more es6y, update QUnit usage, and handle #31
Browse files Browse the repository at this point in the history
 correctly
  • Loading branch information
robertleeplummerjr committed May 16, 2017
1 parent 61af3fa commit 3d2da05
Show file tree
Hide file tree
Showing 18 changed files with 155 additions and 151 deletions.
42 changes: 22 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ gpu.js is a single-file JavaScript library for GPGPU in the browser. gpu.js will
Matrix multiplication written in gpu.js:

```js
var gpu = new GPU();
const gpu = new GPU();

// Create the GPU accelerated function from a kernel
// function that computes a single element in the
// 512 x 512 matrix (2D array). The kernel function
// is run in a parallel manner in the GPU resulting
// in very fast computations! (...sometimes)
var mat_mult = gpu.createKernel(function(A, B) {
const matMult = gpu.createKernel(function(a, b) {
var sum = 0;
for (var i=0; i<512; i++) {
sum += A[this.thread.y][i] * B[i][this.thread.x];
for (var i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).dimensions([512, 512]);
}).setDimensions([512, 512]);

// Perform matrix multiplication on 2 matrices of size 512 x 512
var C = mat_mult(A, B);
const c = matMult(a, b);
```

You can run a benchmark of this [here](http://gpu.rocks). Typically, it will run at 1-15x speedup depending on your hardware.
Expand All @@ -44,13 +44,13 @@ Download the latest version of gpu.js and include the file in your HTML page usi
In JavaScript, initialise the library:

```js
var gpu = new GPU();
const gpu = new GPU();
```

Note that this **requires** the Promise API, if you need to polyfill it, you can give our 'untested polyfill' a try [here](https://github.com/picoded/small_promise.js)

### Creating and Running Functions
Depnding on your output type, specify the intended dimensions of your output. You cannot have a accelerated function that does not specify any dimensions.
Depending on your output type, specify the intended dimensions of your output. You cannot have a accelerated function that does not specify any dimensions.

Dimensions of Output | How to specify dimensions
----------------------- |-------------------------------
Expand All @@ -59,15 +59,15 @@ Dimensions of Output | How to specify dimensions
3D | `[width, height, depth]`

```js
var opt = {
const opt = {
dimensions: [100]
};
```

Create the function you want to run on the GPU. The first input parameter to `createKernel` is a kernel function which will compute a single number in the output. The thread identifiers, `this.thread.x`, `this.thread.y` or `this.thread.z` will allow you to specify the appropriate behavior of the kernel function at specific positions of the output.

```js
var myFunc = gpu.createKernel(function() {
const myFunc = gpu.createKernel(function() {
return this.thread.x;
}, opt);
```
Expand All @@ -82,9 +82,9 @@ myFunc();
Note: Instead of creating an object, you can use the chainable shortcut methods as a neater way of specificying options.

```js
var myFunc = gpu.createKernel(function() {
const myFunc = gpu.createKernel(function() {
return this.thread.x;
}).dimensions([100]);
}).setDimensions([100]);

myFunc();
// Result: [0, 1, 2, 3, ... 99]
Expand All @@ -94,9 +94,9 @@ myFunc();
Kernel functions may accept numbers, or 1D, 2D or 3D array of numbers as input. To define an argument, simply add it to the kernel function like regular JavaScript.

```js
var myFunc = gpu.createKernel(function(x) {
const myFunc = gpu.createKernel(function(x) {
return x;
}).dimensions([100]);
}).setDimensions([100]);

myFunc(42);
// Result: [42, 42, 42, 42, ... 42]
Expand All @@ -105,9 +105,9 @@ myFunc(42);
Similarly, with array inputs:

```js
var myFunc = gpu.createKernel(function(X) {
return X[this.thread.x % 3];
}).dimensions([100]);
const myFunc = gpu.createKernel(function(x) {
return x[this.thread.x % 3];
}).setDimensions([100]);

myFunc([1, 2, 3]);
// Result: [1, 2, 3, 1, ... 1 ]
Expand All @@ -120,13 +120,15 @@ Sometimes, you want to produce a `canvas` image instead of doing numeric computa
For performance reasons, the return value for your function will no longer be anything useful. Instead, to display the image, retrieve the `canvas` DOM node and insert it into your page.

```js
var render = gpu.createKernel(function() {
const render = gpu.createKernel(function() {
this.color(0, 0, 0, 1);
}).dimensions([20, 20]).graphical(true);
})
.setDimensions([20, 20])
.setGraphical(true);

render();

var canvas = render.getCanvas();
const canvas = render.getCanvas();
document.getElementsByTagName('body')[0].appendChild(canvas);
```

Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
"doc": "doc",
"test": "test"
},
"dependencies": {
"browserify": "^14.3.0"
},
"devDependencies": {
"babel-plugin-syntax-async-functions": "^6.5.0",
"browser-sync": "^2.18.2",
Expand All @@ -21,6 +18,7 @@
"gulp-rename": "^1.2.2",
"gulp-uglify": "^1.5.2",
"gulp-util": "^3.0.7",
"qunit-assert-close": "^2.1.2",
"qunitjs": "^2.3.2",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0"
Expand Down
4 changes: 2 additions & 2 deletions src/backend/cpu/function-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const FunctionBuilderBase = require('../function-builder-base');
const CPUFunctionNode = require('./function-node');

module.exports = class CPUFunctionBuilder extends FunctionBuilderBase {
addFunction(functionName, jsFunction, paramTypeArray, returnType) {
addFunction(functionName, jsFunction, paramTypes, returnType) {
this.addFunctionNode(
new CPUFunctionNode(functionName, jsFunction, paramTypeArray, returnType)
new CPUFunctionNode(functionName, jsFunction, paramTypes, returnType)
.setAddFunction(this.addFunction.bind(this))
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/backend/function-builder-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ module.exports = class FunctionBuilderBase {
/// gpu - {GPU} The GPU instance
/// functionName - {String} Function name to assume, if its null, it attempts to extract from the function
/// jsFunction - {JS Function} JS Function to do conversion
/// paramTypeArray - {[String,...]} Parameter type array, assumes all parameters are 'float' if null
/// paramTypes - {[String,...]|{variableName: Type,...}} Parameter type array, assumes all parameters are 'float' if null
/// returnType - {String} The return type, assumes 'float' if null
///
addFunction(functionName, jsFunction, paramTypeArray, returnType) {
addFunction(functionName, jsFunction, paramTypes, returnType) {
throw new Error('addFunction not supported on base');
}

Expand Down
10 changes: 5 additions & 5 deletions src/backend/function-node-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = class BaseFunctionNode {
/// paramTypeArray - {[String,...]} Parameter type array, assumes all parameters are 'float' if null
/// returnType - {String} The return type, assumes 'float' if null
///
constructor(functionName, jsFunction, paramTypeArray, returnType) {
constructor(functionName, jsFunction, paramTypes, returnType) {
//
// Internal vars setup
//
Expand Down Expand Up @@ -88,14 +88,14 @@ module.exports = class BaseFunctionNode {
// Extract parameter name, and its argument types
//
this.paramNames = utils.getParamNamesFromString(this.jsFunctionString);
if (paramTypeArray) {
if (paramTypeArray.length !== this.paramNames.length) {
if (paramTypes) {
if (paramTypes.length !== this.paramNames.length) {
throw 'Invalid argument type array length, against function length -> (' +
paramTypeArray.length + ',' +
paramTypes.length + ',' +
this.paramNames.length +
')';
}
this.paramType = paramTypeArray;
this.paramType = paramTypes;
} else {
this.paramType = [];
for (let a = 0; a < this.paramNames.length; ++a) {
Expand Down
4 changes: 2 additions & 2 deletions src/backend/web-gl/function-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ const FunctionBuilderBase = require('../function-builder-base');
const WebGLFunctionNode = require('./function-node');

module.exports = class WebGLFunctionBuilder extends FunctionBuilderBase {
addFunction(functionName, jsFunction, paramTypeArray, returnType) {
addFunction(functionName, jsFunction, paramTypes, returnType) {
this.addFunctionNode(
new WebGLFunctionNode(functionName, jsFunction, paramTypeArray, returnType)
new WebGLFunctionNode(functionName, jsFunction, paramTypes, returnType)
.setAddFunction(this.addFunction.bind(this))
);
}
Expand Down
6 changes: 3 additions & 3 deletions src/backend/web-gl/function-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ function astErrorOutput(error, ast, funcParam) {
}

module.exports = class WebGLFunctionNode extends FunctionNodeBase {
constructor(functionName, jsFunction, paramTypeArray, returnType) {
super(functionName, jsFunction, paramTypeArray, returnType);
constructor(functionName, jsFunction, paramTypes, returnType) {
super(functionName, jsFunction, paramTypes, returnType);
this.opt = null;
}

Expand Down Expand Up @@ -460,7 +460,7 @@ module.exports = class WebGLFunctionNode extends FunctionNodeBase {

if (!Array.isArray(forNode.init) || forNode.init.length < 1) {
console.log(this.jsFunctionString);
console.warn('Warning: Incompatible for loop declaration');
throw new Error('Error: Incompatible for loop declaration');
}

this.astGeneric(forNode.init, retArr, funcParam);
Expand Down
6 changes: 3 additions & 3 deletions src/gpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,14 @@ module.exports = class GPU {
///
/// Parameters:
/// jsFunction - {JS Function} JS Function to do conversion
/// paramTypeArray - {[String,...]} Parameter type array, assumes all parameters are 'float' if null
/// paramTypes - {[String,...]|{variableName: Type,...}} Parameter type array, assumes all parameters are 'float' if null
/// returnType - {String} The return type, assumes 'float' if null
///
/// Returns:
/// {GPU} returns itself
///
addFunction(jsFunction, paramTypeArray, returnType) {
this._runner.functionBuilder.addFunction(null, jsFunction, paramTypeArray, returnType);
addFunction(jsFunction, paramTypes, returnType) {
this._runner.functionBuilder.addFunction(null, jsFunction, paramTypes, returnType);
return this;
}

Expand Down
1 change: 1 addition & 0 deletions test/html/test-all.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="../../node_modules/qunitjs/qunit/qunit.js"></script>
<script src="../../node_modules/qunit-assert-close/qunit-assert-close.js"></script>

<!-- features -->
<script src="../src/features/promise-api.js"></script>
Expand Down
18 changes: 9 additions & 9 deletions test/src/features/add-custom-function.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function addCustomFunction_sumAB( assert, mode ) {
function addCustomFunction_sumAB(mode) {
var gpu = new GPU();

function custom_adder(a,b) {
Expand All @@ -13,7 +13,7 @@ function addCustomFunction_sumAB( assert, mode ) {
mode : mode
});

assert.ok( f !== null, "function generated test");
QUnit.assert.ok( f !== null, "function generated test");

var a = [1, 2, 3, 5, 6, 7];
var b = [4, 5, 6, 1, 2, 3];
Expand All @@ -22,18 +22,18 @@ function addCustomFunction_sumAB( assert, mode ) {
var exp = [5, 7, 9, 6, 8, 10];

for(var i = 0; i < exp.length; ++i) {
QUnit.close(res[i], exp[i], 0.1, "Result arr idx: "+i);
QUnit.assert.close(res[i], exp[i], 0.1, "Result arr idx: "+i);
}
}

QUnit.test( "addCustomFunction_sumAB (auto)", function( assert ) {
addCustomFunction_sumAB(assert, null);
QUnit.test( "addCustomFunction_sumAB (auto)", function() {
addCustomFunction_sumAB(null);
});

QUnit.test( "addCustomFunction_sumAB (WebGL)", function( assert ) {
addCustomFunction_sumAB(assert, "webgl");
QUnit.test( "addCustomFunction_sumAB (WebGL)", function() {
addCustomFunction_sumAB("webgl");
});

QUnit.test( "addCustomFunction_sumAB (CPU)", function( assert ) {
addCustomFunction_sumAB(assert, "cpu");
QUnit.test( "addCustomFunction_sumAB (CPU)", function() {
addCustomFunction_sumAB("cpu");
});
36 changes: 18 additions & 18 deletions test/src/features/for-loop.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function for_loop_test( assert, mode ) {
function for_loop_test(mode) {
var gpu = new GPU();
var f = gpu.createKernel(function(a, b) {
var x = 0.0;
Expand All @@ -12,7 +12,7 @@ function for_loop_test( assert, mode ) {
mode : mode
});

assert.ok( f !== null, "function generated test");
QUnit.assert.ok( f !== null, "function generated test");

var a = [1, 2, 3, 5, 6, 7];
var b = [4, 5, 6, 1, 2, 3];
Expand All @@ -21,20 +21,20 @@ function for_loop_test( assert, mode ) {
var exp = [15, 17, 19, 16, 18, 20];

for(var i = 0; i < exp.length; ++i) {
QUnit.close(exp[i], res[i], 0.1, "Result arr idx: "+i);
QUnit.assert.close(exp[i], res[i], 0.1, "Result arr idx: "+i);
}
}

QUnit.test( "for_loop (auto)", function( assert ) {
for_loop_test(assert, null);
QUnit.test( "for_loop (auto)", function() {
for_loop_test(null);
});

QUnit.test( "for_loop (WebGL)", function( assert ) {
for_loop_test(assert, "webgl");
QUnit.test( "for_loop (WebGL)", function() {
for_loop_test("webgl");
});

QUnit.test( "for_loop (CPU)", function( assert ) {
for_loop_test(assert, "cpu");
QUnit.test( "for_loop (CPU)", function() {
for_loop_test("cpu");
});

// Prevent test function leak
Expand Down Expand Up @@ -64,33 +64,33 @@ QUnit.test( "for_loop (CPU)", function( assert ) {

var evil_while_exp = evil_while_cpuRef_f(evil_while_a,evil_while_b);

function evil_while_loop_test( assert, mode ) {
function evil_while_loop_test(mode ) {
var gpu = new GPU();

var f = gpu.createKernel(evil_while_kernalFunction, {
dimensions : [6],
mode : mode
});

assert.ok( f !== null, "function generated test");
QUnit.assert.ok( f !== null, "function generated test");

var res = f(evil_while_a,evil_while_b);

for(var i = 0; i < evil_while_exp.length; ++i) {
QUnit.close(evil_while_exp[i], res[i], 0.1, "Result arr idx: "+i);
QUnit.assert.close(evil_while_exp[i], res[i], 0.1, "Result arr idx: "+i);
}
}

QUnit.test( "evil_while_loop (auto)", function( assert ) {
evil_while_loop_test(assert, null);
QUnit.test( "evil_while_loop (auto)", function() {
evil_while_loop_test(null);
});

QUnit.test( "evil_while_loop (WebGL)", function( assert ) {
evil_while_loop_test(assert, "webgl");
QUnit.test( "evil_while_loop (WebGL)", function() {
evil_while_loop_test("webgl");
});

QUnit.test( "evil_while_loop (CPU)", function( assert ) {
evil_while_loop_test(assert, "cpu");
QUnit.test( "evil_while_loop (CPU)", function() {
evil_while_loop_test("cpu");
});

})();
10 changes: 4 additions & 6 deletions test/src/features/function-return.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
function function_return( mode ) {
var gpu = new GPU();
var gpu = new GPU({ mode: mode });
var f = gpu.createKernel(function() {
return 42.0;
}, {
dimensions : [1],
mode : mode
dimensions : [1]
});

QUnit.ok( f !== null, "function generated test");
QUnit.close(f(), 42.0, 0.01, "basic return function test");
QUnit.assert.ok( f !== null, "function generated test");
QUnit.assert.close(f(), 42.0, 0.01, "basic return function test");
}

QUnit.test( "function_return (auto)", function() {
Expand Down

0 comments on commit 3d2da05

Please sign in to comment.