美文网首页我爱编程腾讯云程序员
Javascript如何实现GPU加速?

Javascript如何实现GPU加速?

作者: a3aac2d1b674 | 来源:发表于2018-07-04 15:07 被阅读14次

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

    本文由腾讯Bugly发表于云+社区专栏

    1. 什么是Javascript实现GPU加速?

    CPU与GPU设计目标不同,导致它们之间内部结构差异很大。 CPU需要应对通用场景,内部结构非常复杂。 而GPU往往面向数据类型统一,且相互无依赖的计算。 所以,我们在Web上实现3D场景时,通常使用WebGL利用GPU运算(大量顶点)。 但是,如果只是通用的计算场景呢?比如处理图片中大量像素信息,我们有办法使用GPU资源吗?这正是本文要讲的,GPU通用计算,简称GPGPU。

    2. 实例演示:色块识别

    如下图所示,我们识别图片中彩虹糖色块,给糖果添加表情。

    2.1 实例地址(打开页面后,依次点击按钮“使用CPU计算”、“使用GPU计算”)

    http://tgideas.qq.com/2018/brucewan/gpgpu.html

    2.2 运行代码

    var rgb2hsv = function(r, g, b) {
        var max = Math.max(r, g, b), min = Math.min(r, g, b),
            d = max - min,
            h,
            s = (max === 0 ? 0 : d / max),
            v = max / 255;
            switch (max) {        
            case min: h = 0; break;        
            case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;        
            case g: h = (b - r) + d * 2; h /= 6 * d; break;        
            case b: h = (r - g) + d * 4; h /= 6 * d; break;
        }    
         return {
            h: self.hueIndexs[parseInt(h*360)],
            s: s,
            v: v
        }
    };
    

    运行次数:262144次

    2.3 测试结论

    实例中,我们分别使用GPU和CPU进行色相转换(防止光线影响识别准确度),其余步骤均一致。

    2.4 使用GPGPU意义

    GPU与CPU数据传输过程,与GPU实际运算耗时相当,所以使用GPU运算传输成本过高,实测在Android中具有较大优势。

    本测试案例是从webAR项目中抽取,需要实时跟踪用户摄像头处理视频流(256*256),使用GPU计算意义非常大,否则无法实现实时跟踪。

    3. 如何实现GPU通用计算?

    3.1 首先,我们通过一张流程图,演示原理

    3.2 实现

    3.2.1 创建顶点着色器,只是传递了贴图坐标。

    attribute vec4 position;
    varying vec2 vCoord;void main() {
        vCoord = position.xy * 0.5 + 0.5;
        gl_Position = position;
    }
    

    3.2.2 创建片元着色器,根据贴图坐标贴图。

    precision highp float;
    varying vec2 vCoord;
    uniform sampler2D map;void main(void) {
        vec4 color = texture2D(map, vCoord);
        gl_FragColor = color;
    }
    

    3.3.3 根据如上着色器代码,创建程序对象,变量code是我们要传入的用于计算的代码。

    // 绑定并编译着色器程序var vertexShaderSource = '...';
    var fragmentShaderSource = '...' + code + '...';
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, vertexShaderSource);
    gl.compileShader(vertexShader);
    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, fragmentShaderSource);
    gl.compileShader(fragmentShader);  
                  
    // 创建程序对象
    var program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    gl.useProgram(program);
    

    3.3.4 传入顶点数据,创建一个面覆盖整个画布。

    // 顶点数据传输
    var vertices = new Float32Array([-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0]);
    var vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    var aPosition = gl.getAttribLocation(program, 'position');
    gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(aPosition);
    

    3.3.5 传入原始数据,本例中传入我要处理的图像数据,作为贴图,最终绘制到屏幕。

    var gl = this.gl;
    var program = this.program;
    var texture = gl.createTexture();
    var uMap = gl.getUniformLocation(program, 'map');
    
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.generateMipmap(gl.TEXTURE_2D);
    
    gl.uniform1i(uMap, 0);      
              
    // 绘制
    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
    

    3.3.6 从最终绘制的画面上,获取颜色信息作为最终处理结果数据。

    var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
    gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    

    3.3.7 完整代码: http://tgideas.qq.com/2018/brucewan/gpu.js

    其实清楚原理后,整体实现比较简单。 但是对于不了解WebGL的同学来说,理解上有一定难度,我后续准备写一个系列的WebGL教程,有兴趣的同学可以关注。

    4. 有无现成类库?

    大家可以看到,我实现的gpu.js中,并没有将javascript转换成着色器语言(类C),而是用户直接传入着色器代码。但是github上已有将javascript转换为着色器语言的库。 https://github.com/gpujs/gpu.js

    为什么我没有直接使用呢?

    1. 简单的使用,2k可以实现的代码,不想引入200k的库;
    2. 数据输入输出可以由自己灵活控制;
    3. 着色器语言很简单,特别只是使用基础运算逻辑的代码,没必要由库从Javascript转换。

    没有WebGL基础的同学,建议直接使用https://github.com/gpujs/gpu.js ,从本文理解整体逻辑; 有一定基础的同学,建议由http://tgideas.qq.com/2018/brucewan/gpu.js 自己定制,更为灵活。

    问答
    是否有一种方法以编程方式测试浏览器GPU加速?
    相关阅读
    有哪些主流的科学计算可以利用GPU加速?
    如何使用JavaScript实现GPU加速神经网络
    CPU与GPU区别大揭秘

    此文已由作者授权腾讯云+社区发布,原文链接:https://cloud.tencent.com/developer/article/1148782?fromSource=waitui

    欢迎大家前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~

    相关文章

      网友评论

        本文标题:Javascript如何实现GPU加速?

        本文链接:https://www.haomeiwen.com/subject/dbveuftx.html