Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

writing asm.js by hand

I am trying to add asm.js annotations to the perlin simplex noise function:

"use strict";

// Ported from Stefan Gustavson's java implementation
// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
// Read Stefan's excellent paper for details on how this code works.
//
// Sean McCullough [email protected]

function SimplexNoise(stdlib,foreign,heap) {
    "use asm";
    // heap should be at least 1024*4+12*3*4 bytes long
    var floor = stdlib.Math.floor,
        sqrt = stdlib.Math.sqrt,
        random = new stdlib.Float32Array(heap),
        buf = new stdlib.Int32Array(heap),
        F2 = 0.0, F3 = 0.0,
        G2 = 0.0, G3 = 0.0;
    F2 = (sqrt(3.0)-1.0)*0.5; F3 = 1.0/3.0;
    G2 = (3.0-sqrt(3.0))/6.0; G3 = 1.0/6.0;
    function init() {
        // before calling this function, set the first 256 floats in the heap to be random numbers between 0..1
        // the heap gets rewritten, but if you record the IV separately, the noise is reproducable
        for(var i=0; i<256; i++) {
            buf[i] = buf[i+256] = floor(random[i]*256.0) & 0xff;
            buf[i+512] = buf[i+512+256] = (buf[i] % 12) * 3 + 1024;
        }
        i = 1024;
        buf[i++] = 1;  buf[i++] = 1;  buf[i++] = 0;
        buf[i++] = -1; buf[i++] = 1;  buf[i++] = 0;
        buf[i++] = 1;  buf[i++] = -1; buf[i++] = 0;
        buf[i++] = -1; buf[i++] = -1; buf[i++] = 0;
        buf[i++] = 1;  buf[i++] = 0;  buf[i++] = 1;
        buf[i++] = -1; buf[i++] = 0;  buf[i++] = 1; 
        buf[i++] = 1;  buf[i++] = 0;  buf[i++] = -1;
        buf[i++] = -1; buf[i++] = 0;  buf[i++] = -1;
        buf[i++] = 0;  buf[i++] = 1;  buf[i++] = 1;
        buf[i++] = 0;  buf[i++] = -1; buf[i++] = 1;
        buf[i++] = 0;  buf[i++] = 1;  buf[i++] = -1;
        buf[i++] = 0;  buf[i++] = -1; buf[i++] = -1;
    }
    function noise2D(xin,yin) {
        xin = +xin; yin = +yin;
        var s = 0.0, i = 0, j = 0,
            t = 0.0,
            X0 = 0.0, Y0 = 0.0,
            x0 = 0.0, y0 = 0.0,
            i1 = 0, j1 = 0,
            x1 = 0.0, y1 = 0.0,
            x2 = 0.0, y2 = 0.0,
            ii = 0, jj = 0,
            gi0 = 0, gi1 = 0, gi2 = 0,
            t0 = 0.0, t1 = 0.0, t2 = 0.0,
            n0 = 0.0, n1 = 0.0, n2 = 0.0;
        // Skew the input space to determine which simplex cell we're in 
        s = (xin+yin)*F2; // Hairy factor for 2D 
        i = floor(xin+s); j = floor(yin+s);
        t = (i+j)*G2;
        X0 = i-t; Y0 = j-t; // Unskew the cell origin back to (x;y) space 
        x0 = xin-X0; y0 = yin-Y0; // The x;y distances from the cell origin 
        // For the 2D case; the simplex shape is an equilateral triangle. 
        // Determine which simplex we are in. 
        i1 = (x0>y0?1:0); j1 = (x0>y0?0:1); // Offsets for second (middle) corner of simplex in (i;j) coords 
        // A step of (1;0) in (i;j) means a step of (1-c;-c) in (x;y); and 
        // a step of (0;1) in (i;j) means a step of (-c;1-c) in (x;y); where 
        // c = (3-sqrt(3))/6 
        x1 = x0-i1+G2; y1 = y0-j1+G2; // Offsets for middle corner in (x;y) unskewed coords 
        x2 = x0-1+2*G2; y2 = y0-1+2*G2; // Offsets for last corner in (x;y) unskewed coords 
        // Work out the hashed gradient indices of the three simplex corners 
        ii = i & 255; jj = j & 255;
        gi0 = buf[ii+buf[jj]+512];
        gi1 = buf[ii+i1+buf[jj+j1]+512]; 
        gi2 = buf[ii+1+buf[jj+1]+512];
        // Calculate the contribution from the three corners 
        t0 = 0.5-x0*x0-y0*y0; t1 = 0.5-x1*x1-y1*y1; t2 = 0.5-x2*x2-y2*y2;
        n0 = t0<0.0? 0.0: t0*t0*t0*t0*(buf[gi0]*x0+buf[gi0+1]*y0);  // (x;y) of buf used for 2D gradient 
        n1 = t1<0.0? 0.0: t1*t1*t1*t1*(buf[gi1]*x1+buf[gi1+1]*y1);
        n2 = t2<0.0? 0.0: t2*t2*t2*t2*(buf[gi2]*x2+buf[gi2+1]*y2); 
        // Add contributions from each corner to get the final noise value. 
        // The result is scaled to return values in the interval [-1,1]. 
        return 70.0 * (n0 + n1 + n2);
    }
    return {
        init: init,
        noise2D: noise2D
    };
};

The Firefox Javascript error console is saying only:

Warning: TypeError: asm.js type error: asm.js must end with a return export statement Source File: perlin_simplex.js Line: 9

I have searched for help on what the error can be and find only https://github.com/zbjornson/human-asmjs which is generally very useful, but no help in this specific circumstance.

What have I got wrong? How can this perlin noise function be asm.js-ised by a human?

like image 675
Will Avatar asked Jul 21 '13 23:07

Will


1 Answers

The problem is with the following lines:

F2 = (sqrt(3.0)-1.0)*0.5; F3 = 1.0/3.0;
G2 = (3.0-sqrt(3.0))/6.0; G3 = 1.0/6.0;

Only declarations are allowed in the top level scope of an asm.js module. You're not allowed to define a variable in the top level scope.

According to the spec you may only assign literal values to global variables (amongst other import statements). Hence you can't do the following either because F2, F3, G2 and G3 are not being assigned literal values:

var floor = stdlib.Math.floor,
    sqrt = stdlib.Math.sqrt,
    random = new stdlib.Float32Array(heap),
    buf = new stdlib.Int32Array(heap),
    F2 = (sqrt(3.0)-1.0)*0.5, F3 = 1.0/3.0,
    G2 = (3.0-sqrt(3.0))/6.0, G3 = 1.0/6.0;

Hence you will need to move the definition of F2, F3, G2 and G3 into a function (perhaps the init function). Thus your code should look like:

function SimplexNoise(stdlib,foreign,heap) {
    "use asm";

    var floor = stdlib.Math.floor,
        sqrt = stdlib.Math.sqrt,
        random = new stdlib.Float32Array(heap),
        buf = new stdlib.Int32Array(heap),
        F2 = 0.0, F3 = 0.0,
        G2 = 0.0, G3 = 0.0;

    function init() {
        F2 = (sqrt(3.0)-1.0)*0.5; F3 = 1.0/3.0;
        G2 = (3.0-sqrt(3.0))/6.0; G3 = 1.0/6.0;
        // rest of init
    }

    function noise2D(xin,yin) {
        // function body
    }

    return {
        init: init,
        noise2D: noise2D
    };
};

Human asm.js does tell you about this problem, but they give you the example of an array instead of a simple variable:

Note that these typed arrays cannot be modified outside of a function:

function MyModule(stdlib, foreign, heap) {
  var arr = new stdlib.Int8Array(heap);
  arr[0] = 1; // "asm.js must end with a return export statement"
  // ...
}

Instead do something like this:

function MyModule(stdlib, foreign, heap) {
  var arr = new stdlib.Int8Array(heap);
  function init() {
    arr[0] = 1;
  }

  return {
    init: init
  };
}

There you go. Isn't asm.js a big pain?

like image 192
Aadit M Shah Avatar answered Oct 19 '22 19:10

Aadit M Shah