Skip to content

Commit 7ec1571

Browse files
feat: getPixels method and tests
fix: Add tests for graphical rendering fix: removal of 1d and 3d graphical outputs, they don't make sense fix: Reference of 2 in 2d output in CPU kernel fix: Bump and build
1 parent f4114ec commit 7ec1571

File tree

14 files changed

+897
-284
lines changed

14 files changed

+897
-284
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,25 @@ document.getElementsByTagName('body')[0].appendChild(canvas);
399399
Note: To animate the rendering, use `requestAnimationFrame` instead of `setTimeout` for optimal performance. For more information, see [this](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
400400

401401

402+
### .getPixels() **New in V2!**
403+
To make it easier to get pixels from a context, use `kernel.getPixels()`, which returns a flat array similar to what you get from WebGL's `readPixels` method.
404+
A note on why: webgl's `readPixels` returns an array ordered differently from javascript's `getImageData`.
405+
This makes them behave similarly.
406+
While the values may be somewhat different, because of graphical precision available in the kernel, and alpha, this allows us to easily get pixel data in unified way.
407+
408+
Example:
409+
```js
410+
const render = gpu.createKernel(function() {
411+
this.color(0, 0, 0, 1);
412+
})
413+
.setOutput([20, 20])
414+
.setGraphical(true);
415+
416+
render();
417+
const pixels = render.getPixels();
418+
// [r,g,b,a, r,g,b,a...
419+
```
420+
402421
### Alpha
403422

404423
Currently, if you need alpha do something like enabling `premultipliedAlpha` with your own gl context:

bin/gpu-browser-core.js

Lines changed: 52 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*
55
* GPU Accelerated JavaScript
66
*
7-
* @version 2.0.0-rc.11
8-
* @date Wed Apr 24 2019 17:53:53 GMT-0400 (Eastern Daylight Time)
7+
* @version 2.0.0-rc.12
8+
* @date Fri Apr 26 2019 22:06:12 GMT-0400 (Eastern Daylight Time)
99
*
1010
* @license MIT
1111
* The MIT License
@@ -790,6 +790,12 @@ class CPUKernel extends Kernel {
790790
}
791791
}
792792

793+
if (this.graphical) {
794+
if (this.output.length !== 2) {
795+
throw new Error('Output must have 2 dimensions on graphical mode');
796+
}
797+
}
798+
793799
this.checkOutput();
794800
}
795801

@@ -885,7 +891,7 @@ class CPUKernel extends Kernel {
885891
return function (${ this.argumentNames.map(argumentName => 'user_' + argumentName).join(', ') }) {
886892
${ this._processConstants() }
887893
${ this._processArguments() }
888-
${ this.graphical ? this._graphicalKernelLoop(kernel) : this._resultKernelLoop(kernel) }
894+
${ this.graphical ? this._graphicalKernelBody(kernel) : this._resultKernelBody(kernel) }
889895
${ translatedSources.length > 0 ? translatedSources.join('\n') : '' }
890896
}.bind(this);`;
891897
return kernelString;
@@ -958,18 +964,38 @@ class CPUKernel extends Kernel {
958964
const imageArray = new Array(image.height);
959965
let index = 0;
960966
for (let y = image.height - 1; y >= 0; y--) {
961-
imageArray[y] = new Array(image.width);
967+
const row = imageArray[y] = new Array(image.width);
962968
for (let x = 0; x < image.width; x++) {
963-
const r = pixelsData[index++] / 255;
964-
const g = pixelsData[index++] / 255;
965-
const b = pixelsData[index++] / 255;
966-
const a = pixelsData[index++] / 255;
967-
imageArray[y][x] = [r, g, b, a];
969+
const pixel = new Float32Array(4);
970+
pixel[0] = pixelsData[index++] / 255;
971+
pixel[1] = pixelsData[index++] / 255;
972+
pixel[2] = pixelsData[index++] / 255;
973+
pixel[3] = pixelsData[index++] / 255;
974+
row[x] = pixel;
968975
}
969976
}
970977
return imageArray;
971978
}
972979

980+
getPixels() {
981+
const [width, height] = this.output;
982+
const halfHeight = height / 2 | 0;
983+
const bytesPerRow = width * 4;
984+
const temp = new Uint8Array(width * 4);
985+
const pixels = this._imageData.data.slice(0);
986+
for (let y = 0; y < halfHeight; ++y) {
987+
var topOffset = y * bytesPerRow;
988+
var bottomOffset = (height - y - 1) * bytesPerRow;
989+
990+
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
991+
992+
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
993+
994+
pixels.set(temp, bottomOffset);
995+
}
996+
return pixels;
997+
}
998+
973999
_imageTo3DArray(images) {
9741000
const imagesArray = new Array(images.length);
9751001
for (let i = 0; i < images.length; i++) {
@@ -978,7 +1004,7 @@ class CPUKernel extends Kernel {
9781004
return imagesArray;
9791005
}
9801006

981-
_resultKernelLoop(kernelString) {
1007+
_resultKernelBody(kernelString) {
9821008
switch (this.output.length) {
9831009
case 1:
9841010
return this._resultKernel1DLoop(kernelString) + this._kernelOutput();
@@ -991,14 +1017,10 @@ class CPUKernel extends Kernel {
9911017
}
9921018
}
9931019

994-
_graphicalKernelLoop(kernelString) {
1020+
_graphicalKernelBody(kernelString) {
9951021
switch (this.output.length) {
996-
case 1:
997-
return this._graphicalKernel1DLoop(kernelString) + this._graphicalOutput();
9981022
case 2:
9991023
return this._graphicalKernel2DLoop(kernelString) + this._graphicalOutput();
1000-
case 3:
1001-
return this._graphicalKernel3DLoop(kernelString) + this._graphicalOutput();
10021024
default:
10031025
throw new Error('unsupported size kernel');
10041026
}
@@ -1049,30 +1071,12 @@ class CPUKernel extends Kernel {
10491071
}`;
10501072
}
10511073

1052-
_graphicalKernel1DLoop(kernelString) {
1053-
const {
1054-
output
1055-
} = this;
1056-
const constructorString = this._getKernelResultTypeConstructorString();
1057-
return `${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };`).join('\n') }
1058-
${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new ${constructorString}(${ output[0] });\n`).join('') }
1059-
for (let x = 0; x < ${ output[0] }; x++) {
1060-
this.thread.x = x;
1061-
this.thread.y = 0;
1062-
this.thread.z = 0;
1063-
let kernelResult;
1064-
${ kernelString }
1065-
result[x] = kernelResult;
1066-
${ this._mapSubKernels(subKernel => `result_${ subKernel.name }[x] = subKernelResult_${ subKernel.name };\n`).join('') }
1067-
}`;
1068-
}
1069-
10701074
_resultKernel2DLoop(kernelString) {
10711075
const {
10721076
output
10731077
} = this;
10741078
const constructorString = this._getKernelResultTypeConstructorString();
1075-
return `const result = new Array(${ output[2] });
1079+
return `const result = new Array(${ output[1] });
10761080
${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };`).join('\n') }
10771081
${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(${ output[1] });\n`).join('') }
10781082
for (let y = 0; y < ${ output[1] }; y++) {
@@ -1136,28 +1140,6 @@ class CPUKernel extends Kernel {
11361140
}`;
11371141
}
11381142

1139-
_graphicalKernel3DLoop(kernelString) {
1140-
const {
1141-
output
1142-
} = this;
1143-
const constructorString = this._getKernelResultTypeConstructorString();
1144-
return `${ this._mapSubKernels(subKernel => `let subKernelResult_${ subKernel.name };`).join('\n') }
1145-
${ this._mapSubKernels(subKernel => `const result_${ subKernel.name } = new Array(${ output[2] });\n`).join('') }
1146-
for (let z = 0; z < ${ output[2] }; z++) {
1147-
this.thread.z = z;
1148-
${ this._mapSubKernels(subKernel => `const result_${ subKernel.name }Y = result_${subKernel.name}[z] = new Array(${ output[1] });\n`).join('') }
1149-
for (let y = 0; y < ${ output[1] }; y++) {
1150-
this.thread.y = y;
1151-
${ this._mapSubKernels(subKernel => `const result_${ subKernel.name }X = result_${subKernel.name}Y[y] = new ${constructorString}(${ output[0] });\n`).join('') }
1152-
for (let x = 0; x < ${ output[0] }; x++) {
1153-
this.thread.x = x;
1154-
${ kernelString }
1155-
${ this._mapSubKernels(subKernel => `result_${ subKernel.name }X[x] = subKernelResult_${ subKernel.name };\n`).join('') }
1156-
}
1157-
}
1158-
}`;
1159-
}
1160-
11611143
_kernelOutput() {
11621144
if (!this.subKernels) {
11631145
return 'return result;';
@@ -3585,6 +3567,16 @@ class GLKernel extends Kernel {
35853567
}
35863568
return zResults;
35873569
}
3570+
getPixels() {
3571+
const {
3572+
context: gl,
3573+
output
3574+
} = this;
3575+
const [width, height] = output;
3576+
const pixels = new Uint8Array(width * height * 4);
3577+
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
3578+
return pixels;
3579+
}
35883580
}
35893581

35903582
const renderStrategy = Object.freeze({
@@ -4071,6 +4063,10 @@ class Kernel {
40714063
}
40724064
}
40734065

4066+
getPixels() {
4067+
throw new Error(`"getPixels" called on ${ this.constructor.name }`);
4068+
}
4069+
40744070
checkOutput() {
40754071
if (!this.output || !Array.isArray(this.output)) throw new Error('kernel.output not an array');
40764072
if (this.output.length < 1) throw new Error('kernel.output is empty, needs at least 1 value');

0 commit comments

Comments
 (0)