weblas
Version:
GPU accelerated BLAS for node and the browser
180 lines (138 loc) • 5.55 kB
JavaScript
var WebGL = require('./webgl'),
glslify = require('glslify');
/* A calculator object for the Float texture based GEMM
Generalized Matrix Multiply (GEMM):
C = alpha * A * B + beta * C
where A * B is matrix multiplication
webgl - a weblas.WebGL object
standalone - whether or not to automatically run the floating point encode
step for rendering to an UNSIGNED_BYTE texture (this is required for
mobile, circa 2015) but can't be used as part of a pipeline.
* uploads and downloads data
* executes calculation
*/
function SGEMMCalculator(webgl, standalone){
this.webgl = webgl,
this.standalone = (standalone != null) ? standalone : true; // default to standalone mode
// read GLSL files
var s = glslify('./glsl/sgemm/standalone.glsl'),
s_c = glslify('./glsl/sgemm/standalone_c.glsl'),
p = glslify('./glsl/sgemm/pipeline.glsl'),
p_c = glslify('./glsl/sgemm/pipeline_c.glsl');
// create the webgl shader program for this calculation
// based on the specific fragment shader for this calculation
// and the generic pass through shader
if(this.standalone){
this.program_ = this.webgl.createProgram(s);
this.program_c = this.webgl.createProgram(s_c);
} else {
this.program_ = this.webgl.createProgram(p);
this.program_c = this.webgl.createProgram(p_c);
}
}
module.exports = SGEMMCalculator;
/* Names of the uniforms (variables) used in the shader program passed in on
each calculation.
*/
SGEMMCalculator.TEXTURE_UNIFORM_NAME_0 = "A";
SGEMMCalculator.TEXTURE_UNIFORM_NAME_1 = "B_t";
SGEMMCalculator.TEXTURE_UNIFORM_NAME_2 = "C";
SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME = "K";
SGEMMCalculator.COLUMN_COUNT_UNIFORM_NAME = "N";
SGEMMCalculator.PAD_UNIFORM_NAME = "pad";
SGEMMCalculator.ALPHA_UNIFORM_NAME = "alpha";
SGEMMCalculator.BETA_UNIFORM_NAME = "beta";
/* Calculate the GEMM, with the given data.
M - number of rows in A
N - number of columns in B
K - number of elements in shared dimension (including padding)
alpha - scalar for A
A - left hand matrix (as padded texture)
B - transpose of right hand matrix (as padded texture)
beta - scalar for C
C - additive matrix (texture)
out - output (texture)
How this works:
1. Activate our shader program
2. Bind input textures
3. Set shader program parameters
4. Bind output texture
5. Activate calculation with `drawElements`
TODO: signature should look like this:
( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC )
http://www.math.utah.edu/software/lapack/lapack-blas/dgemm.html
*/
SGEMMCalculator.prototype.calculate = function(M, N, K, alpha, A, B, beta, C, out){
var gl = this.webgl.context;
/*
var h1 = M, w1 = K,
h2 = K, w2 = N;
*/
// set this calculator program as the active program
if(C != null){
this.program = this.program_c;
} else {
beta = null;
this.program = this.program_;
//console.log("no C");
}
this.webgl.selectProgram(this.program);
// bind our input textures containing matrix data
this.bindInputTexture(A, gl.TEXTURE0, SGEMMCalculator.TEXTURE_UNIFORM_NAME_0);
this.bindInputTexture(B, gl.TEXTURE1, SGEMMCalculator.TEXTURE_UNIFORM_NAME_1);
if(C != null){
this.bindInputTexture(C, gl.TEXTURE2, SGEMMCalculator.TEXTURE_UNIFORM_NAME_2);
}
var kPad = this.webgl.getPad(K),
nPad = this.webgl.getPad(N);
// set the data specific variables in our shader program
this.bindUniforms(N, K + kPad, nPad, alpha, beta);
// create our destination texture
if(this.standalone){
this.webgl.bindOutputTexture(M, N + nPad, out);
} else {
this.webgl.bindOutputTexture(M, (N + nPad)/ 4, out);
}
// initiate calculation
gl.drawElements(gl.TRIANGLES, /*num items*/6, gl.UNSIGNED_SHORT, 0);
this.webgl.unbindInputTexture(gl.TEXTURE0);
this.webgl.unbindInputTexture(gl.TEXTURE1);
this.webgl.unbindInputTexture(gl.TEXTURE2);
// result can now be read with gl.readResult, or more operations can be
// performed on destination texture (in pipeline mode)
};
/* Create a texture from the given texel data and bind it to our shader program.
h - number of rows in input matrix
w - number of cols in input matrix
texels - packed data
textureUnit - the texture unit to bind to (gl.TEXTURE0, gl.TEXTURE1, etc)
name - the uniform name to associate with (must match shader program)
must compile program (with createProgram) first
*/
SGEMMCalculator.prototype.bindInputTexture = function(texture, textureUnit, name){
var gl = this.webgl.context,
program = this.program;
gl.activeTexture(textureUnit); // gl.TEXTURE0, gl.TEXTURE1, etc
gl.bindTexture( gl.TEXTURE_2D, texture);
var sampler = gl.getUniformLocation(program, name);
gl.uniform1i(sampler, textureUnit - gl.TEXTURE0);
};
/* Set up inputs for the texture shader
K - size of shared dimension for multiplied matrices
*/
SGEMMCalculator.prototype.bindUniforms = function(N, K, pad, alpha, beta) {
var gl = this.webgl.context;
// get var locations
var K_gl = gl.getUniformLocation(this.program, SGEMMCalculator.SHARED_LENGTH_UNIFORM_NAME),
alpha_gl = gl.getUniformLocation(this.program, SGEMMCalculator.ALPHA_UNIFORM_NAME),
beta_gl = gl.getUniformLocation(this.program, SGEMMCalculator.BETA_UNIFORM_NAME),
N_gl = gl.getUniformLocation(this.program, SGEMMCalculator.COLUMN_COUNT_UNIFORM_NAME),
pad_gl = pad_gl = gl.getUniformLocation(this.program, SGEMMCalculator.PAD_UNIFORM_NAME);
gl.uniform1f(beta_gl, beta);
gl.uniform1i(N_gl, N);
gl.uniform1i(pad_gl, pad);
// bind length of shared dimension
gl.uniform1i(K_gl, K);
// bind alpha
gl.uniform1f(alpha_gl, alpha);
};