gpujs / gpu.js

GPU Accelerated JavaScript
https://gpu.rocks
MIT License
15.08k stars 650 forks source link

Kernel won't execute on GPU!!! #687

Closed dan-reznik closed 3 years ago

dan-reznik commented 3 years ago

I wrote my first GPU.js program (below). Though the graphical output it produces is perfectly correct, it is automatically falling back to CPU mode (no idea why). My PC does have a GPU: Vendor: Google Inc. (NVIDIA), ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0, D3D11-27.21.14.5671)

Would appreciate ideas on how to change the code below so it runs on the GPU. It is live here.

const gpu = new GPU();

function sqr(x) { return x*x; }
function sum3(sides) { return sides[0]+sides[1]+sides[2]; }
function vscale(v,s) { return [s*v[0], s*v[1]]; }
function dist(u,v) { return Math.sqrt(sqr(u[0]-v[0])+sqr(u[1]-v[1])); }
function dist2(u,v) { return sqr(u[0]-v[0])+sqr(u[1]-v[1]); }
function vsum3(uvw) { const [u,v,w]=uvw; return [u[0] + v[0] + w[0], u[1] + v[1] + w[1]]; }

function triAreaHeronSqr(a2, b2, c2) {
  return (2*(a2*b2+b2*c2+c2*a2)-a2*a2-b2*b2-c2*c2)/16;
}

function bary_X16_sqr(a2,b2,c2) {
  const sqrt3 = 1.7320508075688772;
  const area2 = triAreaHeronSqr(a2, b2, c2);
  const twoS = 4 * Math.sqrt(area2);
  const v1 = a2 * (twoS + (a2 - b2 - c2) * sqrt3);
  const v2 = b2 * (twoS + (-a2 + b2 - c2) * sqrt3);
  const v3 = c2 * (twoS + (-a2 - b2 + c2) * sqrt3);
  return [v1, v2, v3];
}

function tri_sides_sqr(p1,p2,p3) {
  const s1 = dist2(p2, p3);
  const s2 = dist2(p3, p1);
  const s3 = dist2(p1, p2);
  return [s1, s2, s3];
}

function barys_to_cartesian(p1,p2,p3,bs) {
  const bs_sum = sum3(bs);
  const tri_scaled = [vscale(p1,bs[0]),vscale(p2,bs[1]),vscale(p3,bs[2])];
  const tri_scaled_norm = vscale(vsum3(tri_scaled), 1 / bs_sum);
  return tri_scaled_norm;
}

function get_X16(p1,p2,p3) {
  const sides2 = tri_sides_sqr(p1,p2,p3);
  const bs = bary_X16_sqr(sides2[0],sides2[1],sides2[2]);
  return barys_to_cartesian(p1,p2,p3,bs);
}

function get_X16_map(t0,t1,t2,M) {
    const p = get_X16(M,t1,t2);
    const q = get_X16(M,t2,t0);
    const r = get_X16(M,t0,t1);
    return [p,q,r];
}

function tri_dist_sqr(p1,p2,p3,q1,q2,q3) { return dist2(p1,q1)+dist2(p2,q2)+dist2(p3,q3); }

function get_triple_X16_map_error(t0,t1,t2,M) {
    const [x1,y1,z1] = get_X16_map(t0,t1,t2,M);
    const [x2,y2,z2] = get_X16_map(x1,y1,z1,M);
    const [x3,y3,z3] = get_X16_map(x2,y2,z2,M);
    const d2 = tri_dist_sqr(t0,t1,t2,x3,y3,z3);
    return d2;
}

const render = gpu.createKernel(function(tri) {
   let dim = 1024; // this.constants.dim won't work
   let half_dim = dim>>1;
   let max = 5; // this.constants.max won't work
   let i = this.thread.x;
   let j = this.thread.y;
   let x = max*(i-half_dim)/dim;
   let y = max*(j-half_dim)/dim;

  let err = get_triple_X16_map_error(tri[0],tri[1],tri[2], [x, y]);
  if (err<1e-9) this.color(0,0,1, 1); else this.color(.9,.9,.9, 1);
}, {
  argumentTypes: { tri:'Array1D(2)' },
})
.setConstants({ dim: 1024, max: 5 })
.setGraphical(true)
.setOutput([1024,1024]);

// equilateral triangle
const reg3 = [[1,0],[-.5,.866025],[-.5,-.866025]]; 
render(reg3);

const canvas = render.canvas;
document.getElementsByTagName('body')[0].appendChild(canvas);
robertleeplummerjr commented 3 years ago

The issue is that you are using unsigned (4 8bit integers, packed to GPU, unpacked, and repacked for storage of a lower precision 32 bit float) output. You likely could get past it by just using 'Array' as the argument/return types, or just omit those altogether, and let GPU.js detect the types. I'm working out a fix.

dan-reznik commented 3 years ago

Robert thanks! can I simply declare that everything is single?

There is this tension of not declaring anything and declaring everything which has caused me (a newbie) a lot of trouble. Also, would appreciate if could be more clear in the documentation about what is the difference of

a) functions declared normally before the gpu.createKernel() b) gpu.addFunction() c) kernel.addFunction() d) native functions e) injected functions

When should we need either?

Dan

On Wed, Apr 14, 2021 at 11:13 AM Robert Plummer @.***> wrote:

The issue is that you are using unsigned (4 8bit integers, packed to GPU, unpacked, and repacked for storage of a lower precision 32 bit float) output. You likely could get past it by just using 'Array' as the argument/return types, or just omit those altogether, and let GPU.js detect the types. I'm working out a fix.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/gpujs/gpu.js/issues/687#issuecomment-819550497, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD32VFRWIJS3PSGFFCCPWBLTIWPHHANCNFSM425CUJPA .

robertleeplummerjr commented 3 years ago

can I simply declare that everything is single?

Not for when in graphical mode.

Also, would appreciate if could be more clear in the documentation

Much of that came about from the project being incomplete when I found it. I'm just a maintainer.

When should we need either?

I personally use gpu.addFunction(), I feel it is the most descriptive.

I ran your use case and found that it looks like you are doing some advanced stuff with matrices, which is highly experimental. I'd try and stick to simple arrays if possible unless you'd like to be a contributor as well, which is always welcomed.

In any case, I provided a fix which allows us to use Array(2), Array(3), and Array(4) in both unsigned and single precision use cases. That was just published: https://www.npmjs.com/package/gpu.js/v/2.11.3

dan-reznik commented 3 years ago

Robert thanks for the update. I just tried it and it is still falling back to the CPU. Perhaps I did not update it correctly. I am using this from within the browser. I am loading the library from index.html with:

<script @.***/dist/gpu-browser.min.js">

Will this get me to the latest version?

Dan

On Wed, Apr 14, 2021 at 5:55 PM Robert Plummer @.***> wrote:

can I simply declare that everything is single?

Not for when in graphical mode.

Also, would appreciate if could be more clear in the documentation

Much of that came about from the project being incomplete when I found it. I'm just a maintainer.

When should we need either?

I personally use gpu.addFunction(), I feel it is the most descriptive.

I ran your use case and found that it looks like you are doing some advanced stuff with matrices, which is highly experimental. I'd try and stick to simple arrays if possible unless you'd like to be a contributor as well, which is always welcomed.

In any case, I provided a fix which allows us to use Array(2), Array(3), and Array(4) in both unsigned and single precision use cases. That was just published: https://www.npmjs.com/package/gpu.js/v/2.11.3

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

dan-reznik commented 3 years ago

I am not trying to do anything with matrices. This is geometry code, it has arrays of points (I think this is called Array1D(2)), e.g., representing the 3 vertices of a triangle.

On Wed, Apr 14, 2021 at 5:55 PM Robert Plummer @.***> wrote:

can I simply declare that everything is single?

Not for when in graphical mode.

Also, would appreciate if could be more clear in the documentation

Much of that came about from the project being incomplete when I found it. I'm just a maintainer.

When should we need either?

I personally use gpu.addFunction(), I feel it is the most descriptive.

I ran your use case and found that it looks like you are doing some advanced stuff with matrices, which is highly experimental. I'd try and stick to simple arrays if possible unless you'd like to be a contributor as well, which is always welcomed.

In any case, I provided a fix which allows us to use Array(2), Array(3), and Array(4) in both unsigned and single precision use cases. That was just published: https://www.npmjs.com/package/gpu.js/v/2.11.3

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

dan-reznik commented 3 years ago

To try to debug why I cannot yet invoke the GPU,

0) perhaps I still don't have the fixed version 1) How can I know which types are being assigned to each function? 2) Currently, i don't gpu.addFunction() anywhere. Could this be a problem?

In this repository one only needs to files: index.html and main.js

https://github.com/dan-reznik/Isodynamic-Map-GPU

On Wed, Apr 14, 2021 at 9:33 PM Dan Reznik @.***> wrote:

I am not trying to do anything with matrices. This is geometry code, it has arrays of points (I think this is called Array1D(2)), e.g., representing the 3 vertices of a triangle.

On Wed, Apr 14, 2021 at 5:55 PM Robert Plummer @.***> wrote:

can I simply declare that everything is single?

Not for when in graphical mode.

Also, would appreciate if could be more clear in the documentation

Much of that came about from the project being incomplete when I found it. I'm just a maintainer.

When should we need either?

I personally use gpu.addFunction(), I feel it is the most descriptive.

I ran your use case and found that it looks like you are doing some advanced stuff with matrices, which is highly experimental. I'd try and stick to simple arrays if possible unless you'd like to be a contributor as well, which is always welcomed.

In any case, I provided a fix which allows us to use Array(2), Array(3), and Array(4) in both unsigned and single precision use cases. That was just published: https://www.npmjs.com/package/gpu.js/v/2.11.3

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.