gpujs / gpu.js

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

Minfied Kernel Functions Do Not Work #646

Open harshkhandeparkar opened 3 years ago

harshkhandeparkar commented 3 years ago

meme

https://harshkhandeparkar.github.io/rainbow-board is not working.

But if you clone the repo and yarn start, it works in react development server. I have no idea what to do.

harshkhandeparkar commented 3 years ago

I did some experimentation.

Observations

  1. Changing the gpu.js version does nothing.
  2. Included gpu.js from public/ in index.html instead of node_modules. Nothing.
  3. GPUjs Real Renderer works WITHOUT minification of dist.
  4. Tried using the minified dist in the default demo of gpujs-real-renderer, DOESN'T WORK.
  5. Older versions of Real Renderer which had a different kernel WORK with and without minification.

Conclusions

  1. Real Renderer is fine.
  2. React is fine.
  3. GPU.js DOES NOT work with MINIFIED kernel functions in CERTAIN CASES ONLY.

@robertleeplummerjr You should seriously take a look at this.

Thank you.

harshkhandeparkar commented 3 years ago

Reference Kernels: https://github.com/HarshKhandeparkar/gpujs-real-renderer/blob/f83ce97fe1e644f0c9c50bb51d34b6719fa38c04/src/kernels/plot.ts#L29-L76

https://github.com/HarshKhandeparkar/gpujs-real-renderer/blob/f83ce97fe1e644f0c9c50bb51d34b6719fa38c04/src/kernels/interpolate.ts#L29-L118

midnight-dev commented 3 years ago

Minified code can sometimes suffer from a non-uniform/asymmetric conversion of conditionals and implicit semicolons. For instance, a minifier optimistically concatenates all lines, and a missing semicolon will result in JavaScript engines trying to eat themselves. It's normally easy to spot, but you're compiling from TypeScript to JavaScript and feeding a minified, custom function to the library. That's not guaranteed to cause an obvious console error.

To see if it's a uniformity issue, I'd suggest taking your compiled & minified code, format it with something like beautify/prettier, and look to see if there are any unexpected artifacts like incorrect concatenation or wrongly simplified logic/conditional statements.

I hope that's what's going on here. Otherwise, there's some dark magic at play inside the createKernel method. I'd like to hear from @robertleeplummerjr on this as well if the minified code turns out to be perfectly symmetrical.

harshkhandeparkar commented 3 years ago

I was trying to find what is causing this error. I did not think of ASI though. I'll try that now, ty!

harshkhandeparkar commented 3 years ago

ASI is a negative.

image This is weird because there is no && in that kernel.

harshkhandeparkar commented 3 years ago

Anyway I need to improve the kernels because they have too much logic.

harshkhandeparkar commented 3 years ago

@midnight-dev 's beautification suggestion was brilliant.

And the issue is.... drumroll.... Javascript -_-

Javascript has this stupid use of && where it can be used as a logical as well as the following nonsense. image

harshkhandeparkar commented 3 years ago

Left is beautified minified code and right is the real kernel.

harshkhandeparkar commented 3 years ago

Looks like I will have to convert logic entirely into Math. methods :grimacing:

harshkhandeparkar commented 3 years ago

image This fix works fine :slightly_smiling_face:. I am proud of myself :stuck_out_tongue:

harshkhandeparkar commented 3 years ago

But that is only one of the two kernels :hot_face:

harshkhandeparkar commented 3 years ago

image I have no idea what this means.

harshkhandeparkar commented 3 years ago

There is this simple thing I can do which is to convert the kernel to a string. But that is a bit cumbersome. (but it works)

midnight-dev commented 3 years ago

Oof. Yeah, when I saw that error, I suspected the minifier made use of && in the less intuitive manner.

&& allows you to test if both the left and right values are true, but it also means that it evaluates everything in order. So as long as the first value is truthy, whatever is to the right will run. It's sometimes used in regular JavaScript development but is super common in minified JS. Much of the time, it's a symmetric and smaller version of nested if conditionals. However, sometimes it and its || counterpart can have buggy artifacts, especially when it's assigning a value in the middle or end of the line.

If the minifier is combining nested conditionals, you should be able to prevent it by inserting a command inside the second to last conditional and just outside the last inner nested if statement. Console logging may slow things down if it happens a lot, so I'd sooner set something like a counter variable and increment it during each iteration and maybe log it at the very end. The minified code won't be able to remove it since it'll be referenced later, and it won't be able to combine everything because the intensity can only be modified if your brush size condition succeeds.

Just noticed your newer comments. Do you create, modify, or check any arrays? Or are you using an object/function anywhere that may need to be an array?

midnight-dev commented 3 years ago

Addendum: I read the console error incorrectly. It's not complaining that the length is undefined - it's saying that the array itself is undefined.

It's tricky to debug minified code. It's typically avoided with blackboxing. But since this phenomena is only occurring with minification, it may be the only way to reliably hunt down the problem.

Open the debugger, find the script, and hit the format/beautify button { } - it should be near the bottom of the debugger/sources panel when the minified JavaScript is selected. Switch to the console and refresh / run the page again to get the error. On the right of the console, click on the link (i.e. gpu-browser.min.js) associated with the error, and it will hopefully bring you to the problem area inside the debugger. Create a breakpoint at or just before the fatal error and rerun the script. You'll be able to see the problematic variable referenced somewhere. If you can't find wherever the variable is getting set, use the call stack (typically somewhere on the right) to trace backward until you see it. You may have to set additional breakpoints before the fatal error so you can step through the script.

My apologies if you're already an experienced debugger. Wanted to briefly go over the basic concept since it really comes in handy during moments of confusion & frustration - both of which are features of JavaScript in general.

harshkhandeparkar commented 3 years ago

1) I am not modifying any arrays, I am just returning an array. (But I am also returning an array in the other kernel where this error did not occur) 2) Ty. I will try that out :)