View on GitHub

Webgl-matrix-demo

High performance matrix multiplication in Javascript vs WebGL

Download this project as a .zip file Download this project as a tar.gz file

Welcome to GitHub Pages.

Matrix Multiplication Test in WebGL

This demo shows the difference in performance between Javascript and gpu performance when naively multiplying some matrices.

// fragment shader that calculates the sum of the passed row and     
// column (texture coord). 
// we loop over the row and column and sum the product. 
// product is then rendered to 32-bit IEEE754 floating point in the 
// output RGBA canvas. 
// readPixel is used to read the bytes. 
#ifdef GL_ES 
  precision highp float; 
#endif 

  varying vec2    vTex;         // row, column to calculate 
  uniform sampler2D usampler;   // left in .r, right in .g 
  uniform int     uLength;      // r1xc1.r2xc2 => product has r2 (or c1) terms 
  uniform float   uStepS;       // increment across source texture 
  uniform float   uStepT;       // increment down source texture 
  uniform float   uOutRows;     // size of output in rows 
  uniform float   uOutCols;     // size of output in columns 
   
  // sum row r x col c 
  float sumrowcol(float row, float col) { 
    float sum = 0.;             // sum 
    float ss = 0.;              // column on source texture 
    float tt = 0.;              // row on source texture 
    float r = row*uStepT;       // moving texture coordinate 
    float c = col*uStepS;       // moving texture coordinate 
    for (int pos=0 ; pos<2048 ; ++pos) { 
      if(pos>=uLength) break; // stop when we multiple a row by a column 
      float m1 = texture2D(usampler,vec2(ss,r)).r; 
      float m2 = texture2D(usampler,vec2(c,tt)).g; 
      sum += (m1*m2); 
      ss += uStepS; 
      tt += uStepT; 
    } 
    return sum; 
  } 
   
  void main(void) { 
     
    // get the implied row and column from .s and .t of passed texel 
    float col = floor((vTex.s*uOutRows)); 
    float row = floor((vTex.t*uOutCols));    

    // sum row x col for the passed pixel 
    float v = sumrowcol(row,col); 

    // Render to IEEE 754 Floating Point 
    if (v==0.) { 
      gl_FragColor = vec4(0.,0.,0.,0.); 
      return; 
    } 
    float a = abs(v);                           // encode absolute value + sign 
    float exp = floor(log2(a));                 // number of powers of 2 
    float mant = (a * pow(2.,23.-exp));         // multiply to fill 24 bits (implied leading 1) 
    float mant1 = floor(mant / 256. / 256.);    // first 8 bits of mantissa 
    float mant2 = mod(floor(mant / 256.),256.); // second 8 bits 
    float mant3 = mod(mant,256.);               // third 8 bits 
     
    highp float sign = 128.-128.*(a/v);     // sign bit is 256 or 0 
    highp float e = (sign+exp+127.)/510.;   // exponent and sign 
    highp float m1 = (mant1-(128.*(1.-mod(exp+127.,2.))))/255.; // handle leading bit 
    highp float m2 = (mant2)/255.;        // middle part 
    highp float m3 = (mant3+.5)/255.;     // scale to 0 - 255 
    gl_FragColor = vec4(m3,m2,m1,e);      // output an IEEE754 32-bit floating point number 
  } 
  

Hit the test button to run. Typically, it'll take about 5 secs to multiply the larger matrices in Javascript.