gpu.js/src/backend/fallback.js
2016-02-28 16:03:01 +08:00

202 lines
4.5 KiB
JavaScript

(function (GPU) {
function clone(obj) {
if(obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
return obj;
var temp = obj.constructor(); // changed
for(var key in obj) {
if(Object.prototype.hasOwnProperty.call(obj, key)) {
obj.isActiveClone = null;
temp[key] = clone(obj[key]);
delete obj.isActiveClone;
}
}
return temp;
}
function getArgumentType(arg) {
if (Array.isArray(arg)) {
return 'Array';
} else if (typeof arg == "number") {
return 'Number';
} else if (arg instanceof GPUTexture) {
return 'Texture';
} else {
return 'Unknown';
}
}
/// JS fallback transformation, basically pure JS
///
/// @param inputFunction The calling to perform the conversion
/// @param opt The parameter object
///
/// @returns callable function if converted, else returns null
GPU.prototype._backendFallback = function(kernel, opt) {
var gpu = this;
function ret() {
if (!opt.dimensions || opt.dimensions.length === 0) {
if (arguments.length != 1) {
throw "Auto dimensions only supported for kernels with only one input";
}
var argType = getArgumentType(arguments[0]);
if (argType == "Array") {
opt.dimensions = getDimensions(argType);
} else if (argType == "Texture") {
opt.dimensions = arguments[0].dimensions;
} else {
throw "Auto dimensions not supported for input type: " + argType;
}
}
var kernelArgs = [];
for (var i=0; i<arguments.length; i++) {
var argType = getArgumentType(arguments[i]);
if (argType == "Array" || argType == "Number") {
kernelArgs[i] = arguments[i];
} else if (argType == "Texture") {
kernelArgs[i] = arguments[i].toArray();
} else {
throw "Input type not supported: " + arguments[i];
}
}
var threadDim = clone(opt.dimensions);
while (threadDim.length < 3) {
threadDim.push(1);
}
var ret = new Array(threadDim[2]);
for (var i=0; i<threadDim[2]; i++) {
ret[i] = new Array(threadDim[1]);
for (var j=0; j<threadDim[1]; j++) {
ret[i][j] = new Array(threadDim[0]);
}
}
var canvas;
var canvasCtx;
var imageData;
var data;
if (opt.graphical) {
canvas = gpu.getCanvas('cpu');
canvas.width = threadDim[0];
canvas.height = threadDim[1];
canvasCtx = canvas.getContext("2d");
imageData = canvasCtx.createImageData(threadDim[0], threadDim[1]);
data = new Uint8ClampedArray(threadDim[0]*threadDim[1]*4);
}
var ctx = {
thread: {
x: 0,
y: 0,
z: 0
},
dimensions: {
x: threadDim[0],
y: threadDim[1],
z: threadDim[2]
}
};
ctx.color = function(r, g, b, a) {
if (a == undefined) {
a = 1.0;
}
r = Math.floor(r * 255);
g = Math.floor(g * 255);
b = Math.floor(b * 255);
a = Math.floor(a * 255);
var width = ctx.dimensions.x;
var height = ctx.dimensions.y;
var x = ctx.thread.x;
var y = height - ctx.thread.y - 1;
var index = x + y*width;
data[index*4+0] = r;
data[index*4+1] = g;
data[index*4+2] = b;
data[index*4+3] = a;
};
for (ctx.thread.z=0; ctx.thread.z<threadDim[2]; ctx.thread.z++) {
for (ctx.thread.y=0; ctx.thread.y<threadDim[1]; ctx.thread.y++) {
for (ctx.thread.x=0; ctx.thread.x<threadDim[0]; ctx.thread.x++) {
ret[ctx.thread.z][ctx.thread.y][ctx.thread.x] = kernel.apply(ctx, kernelArgs);
}
}
}
if (opt.graphical) {
imageData.data.set(data);
canvasCtx.putImageData(imageData, 0, 0);
}
if (opt.dimensions.length == 1) {
ret = ret[0][0];
} else if (opt.dimensions.length == 2) {
ret = ret[0];
}
return ret;
}
ret.dimensions = function(dim) {
opt.dimensions = dim;
return ret;
};
ret.debug = function(flag) {
opt.debug = flag;
return ret;
};
ret.graphical = function(flag) {
opt.graphical = flag;
return ret;
};
ret.loopMaxIterations = function(max) {
opt.loopMaxIterations = max;
return ret;
};
ret.wraparound = function() {
opt.wraparound = false;
return ret;
};
ret.hardcodeConstants = function() {
opt.hardcodeConstants = false;
return ret;
};
ret.outputToTexture = function() {
opt.outputToTexture = false;
return ret;
};
ret.mode = function(mode) {
opt.mode = mode;
return gpu.createKernel(kernel, opt);
};
ret.getCanvas = function() {
return gpu.getCanvas('cpu');
};
return ret;
};
})(GPU);