gpujsutils

Wrapper for gpujs (with a stable slightly edited gpu.js source file) with a bunch of premade functions for use or demonstration. The main show here is bulk GPU FFTs which run REALLY fast.

Usage no npm install needed!

<script type="module">
  import gpujsutils from 'https://cdn.skypack.dev/gpujsutils';
</script>

README

gpujsutils

gpu.js is amazing and this makes life easier to use it and add baked but flexible functionality. This revolve around persistent kernels with resizable i/o on-the-fly so we can make the best use of the performance benefits of parallelization. This is even better with web workers (see our MagicWorker library)

npm i gpujsutils

Create a GPU util instance, + general utility functions

import {gpuUtils, makeKrnl, makeCanvasKrnl} from 'gpuUtils'

const gpuutils = new gpuUtils();

Create a GPU kernel with default settings


function transpose2DKern(mat2) { //Transpose a 2D matrix, meant to be combined
    return mat2[gpuutils.thread.y][gpuutils.thread.x];
}

let kernel = makeKrnl(
    gpuutils.gpu, //the actual GPUjs instance, used normally  
    transpose2DKern, //the kernel
{ //kernel settings, meant to use all of the flexibility features by default (e.g. dynamic sizing)
  setDynamicOutput: true,
  setDynamicArguments: true,
  setPipeline: true,
  setImmutable: true,
  setGraphical: false
}); //makeKrnl(gpuutils.gpu);

let mat2 = [[1,2,3,4],[5,6,7,8],[8,9,10,11],[12,13,14,15]];

let result = kernel(mat2)

//OR to add to the gpu utilities

kernel = gpuutils.addKernel('transpose2D', transpose2DKern);
result = gpuutils.callKernel('transpose2D',mat2);


Create a kernel that renders to a canvas


//From a gpujs observable: https://observablehq.com/@robertleeplummerjr/video-convolution-using-gpu-js
function ImgConv2DKern(img, width, height, kernel, kernelLength) {
    let kernelRadius = (Math.sqrt(kernelLength) - 1) / 2;
    const kSize = 2 * kernelRadius + 1;
    let r = 0, g = 0, b = 0;

    let i = -kernelRadius;
    let kernelOffset = 0;
    while (i <= kernelRadius) {
        if (gpuutils.thread.x + i < 0 || gpuutils.thread.x + i >= width) {
            i++;
            continue;
        }

        let j = -kernelRadius;
        while (j <= kernelRadius) {
            if (gpuutils.thread.y + j < 0 || gpuutils.thread.y + j >= height) {
                j++;
                continue;
            }

            kernelOffset = (j + kernelRadius) * kSize + i + kernelRadius;
            const weights = kernel[kernelOffset];
            const pixel = img[gpuutils.thread.y + i][gpuutils.thread.x + j];
            r += pixel.r * weights;
            g += pixel.g * weights;
            b += pixel.b * weights;
            j++;
        }
        i++;
    }

    gpuutils.color(r, g, b);
}


let kernel = makeCanvasKernel( 
    gpu,
    ImgConv2DKern,
    {
        output: [300,300],
        setDynamicArguments: true,
        setDynamicOutput: true,
        setPipeline: false,
        setImmutable: true,
        setGraphical: true
    },
    divId //id of the div to append a new canvas to. Leave blank to append to body
);

kernel(video); //input an image or video from a source

//OR to add to the gpu utilities

kernel = gpuutils.addCanvasKernel('imgConv', ImgConv2DKern);
gpuutils.callCanvasKernel('imgConv',video, [video.width,video.height]);



Combine Kernels


//adapted from gpujs tutorial
const add = gpuutils.addKernel('add',function(a, b) {
  return a[gpuutils.thread.x] + b[gpuutils.thread.x];
}).setOutput([20]);

const multiply = gpuutils.addKernel('multiply',function(a, b) {
  return a[gpuutils.thread.x] * b[gpuutils.thread.x];
}).setOutput([20]);

//multi-step operations
const superKernel = gpuutils.combineKernels(
    'superKernel', //name the combined kernel
    ['add','multiply'], 
    function(a, b, c) {
        return multiply(add(a, b), c);
    }
);

let result = gpuutils.callKernel('superKernel',[3,4,5]);

Default Macros


//pass signal buffer to receive an ordered amplitude spectrum based 0.5x the size of sample rate (nyquist frequency)
gpuutils.gpuDFT(
    signalBuffer, //the signal buffer
    nSeconds, //number of seconds of data. sample rate = length/nSeconds 
    scalar, //can apply a scalar multiplier to the amplitudes
    texOut //receive a texture map out instead of arrays?
);

//or
gpuutils.gpuFFT(...); //faster


// pass a 1D array of evenly-sized signal buffers to receive a list of ordered amplitude spectrums 0.5x the size of sample rate (nyquist frequency)
gpuutils.MultiChannelDFT(
    signalBuffer,  //1D list of evenly sized signal buffers
    nSeconds, //number of seconds of data. sample rate = length/nSeconds 
    scalar, //can apply a scalar multiplier to the amplitudes
    false //receive a texture map out instead of arrays?
);

//or 
gpuutils.MultiChannelFFT(...); //faster

// pass a 1D array of evenly-sized signal buffers to receive a list of ordered amplitude spectrums 0.5x the size of sample rate (nyquist frequency), between the two frequencies. Better with more seconds or higher samplerate
gpuutils.MultiChannelDFT_Bandpass(
    signalBuffer,  //1D list of evenly sized signal buffers
    nSeconds, //number of seconds of data. sample rate = length/nSeconds 
    freqStart, //start frequency e.g. 3Hz
    freqEnd, //end Frequency e.g. 100Hz
     scalar, //can apply a scalar multiplier to the amplitudes
    false //receive a texture map out instead of arrays?
);

//or 
gpuutils.MultiChannelFFT_BandPass(...);

Default Kernels


    gpuutils.dft //discrete fourier transform
    gpuutils.idft //inverse DFT (untested :P)
    gpuutils.dft_windowed //DFT between two frequencies, need sufficient sample rate or number of seconds of data
    gpuutils.idft_windowed //inverse dft to reverse the bandpassed dft (untested)
    gpuutils.fft //fast fourier transform, simply decimates the number of samples used to compute lower frequency amplitudes
    gpuutils.ifft //inverse fft (untested)
    gpuutils.fft_windowed //bandpassed fft
    gpuutils.ifft_windowed  //inverse bandpassed fft (untested)
    gpuutils.listdft2D  //pass an array of arrays in of the same length to return a list of DFTs (broken) 
    gpuutils.listdft1D   //pass a single array of evenly sized sample sets of data to return the DFTs. 
    gpuutils.listdft1D_windowed  //etc
    gpuutils.listfft1D             //etc
    gpuutils.listfft1D_windowed     //tc
    gpuutils.listidft1D_windowed  //untested
    gpuutils.listifft1D_windowed  //untested
    gpuutils.bulkArrayMul 
    gpuutils.correlograms; //cross correlations, untested
    gpuutils.correlogramsPC; //precomputed means and estimators

There's a few other things used internally but they're not that useful.

Joshua Brewster & Dovydas Stirpeika License: AGPL v3.0