为了账号安全,请及时绑定邮箱和手机立即绑定

Javascript如何实现GPU加速?

标签:
JavaScript

一、什么是Javascript实现GPU加速?

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

二、实例演示:色块识别。

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

https://img1.sycdn.imooc.com//5b0bd87a00012daa05110488.jpg https://img1.sycdn.imooc.com//5b0bd89a0001517e05070508.jpg

2.1、实例地址(打开页面后,依次点击按钮“使用CPU计算”、“使用GPU计算”):
http://tgideas.qq.com/2018/brucewan/gpgpu.html

2.2、运行代码:

复制代码

 1 var rgb2hsv = function(r, g, b) { 2     var max = Math.max(r, g, b), min = Math.min(r, g, b), 3         d = max - min, 4         h, 5         s = (max === 0 ? 0 : d / max), 6         v = max / 255; 7     switch (max) { 8         case min: h = 0; break; 9         case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;10         case g: h = (b - r) + d * 2; h /= 6 * d; break;11         case b: h = (r - g) + d * 4; h /= 6 * d; break;12     }13     return {14         h: self.hueIndexs[parseInt(h*360)],15         s: s,16         v: v17     }18 };

复制代码

  运行次数:262144次

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

测试平台测试结论
PCGPU较CPU优势较少
iOSGPU较CPU优势较少
AndroidvivoX20(运行10次平均)
CPU:770ms,GPU:270
GPU较CPU快2.85倍
三星S7(运行10次平均)
CPU:982ms,GPU:174ms
GPU较CPU快5.64倍

 

 2.4、使用GPGPU意义:
GPU与CPU数据传输过程,与GPU实际运算耗时相当,所以使用GPU运算传输成本过高,实测在Android中具有较大优势。
本测试案例是从webAR项目中抽取,需要实时跟踪用户摄像头处理视频流(256*256),使用GPU计算意义非常大,否则无法实现实时跟踪。

 

三、如何实现GPU通用计算?

3.1、首先,我们通过一张流程图,演示原理:
https://img1.sycdn.imooc.com//5b0bd8ae00012c4205730565.jpg

3.2、实现:

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

复制代码

1 attribute vec4 position;2 varying vec2 vCoord;3 void main() {4     vCoord = position.xy * 0.5 + 0.5;5     gl_Position = position;6 }

复制代码

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

复制代码

1 precision highp float;2 varying vec2 vCoord;3 uniform sampler2D map;4 void main(void) {5     vec4 color = texture2D(map, vCoord);6     gl_FragColor = color;7 }

复制代码

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

复制代码

 1 // 绑定并编译着色器程序 2 var vertexShaderSource = '...'; 3 var fragmentShaderSource = '...' + code + '...'; 4 var vertexShader = gl.createShader(gl.VERTEX_SHADER); 5 gl.shaderSource(vertexShader, vertexShaderSource); 6 gl.compileShader(vertexShader); 7 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); 8 gl.shaderSource(fragmentShader, fragmentShaderSource); 9 gl.compileShader(fragmentShader);                
10 11 // 创建程序对象12 var program = gl.createProgram();13 gl.attachShader(program, vertexShader);14 gl.attachShader(program, fragmentShader);15 gl.linkProgram(program);16 gl.useProgram(program);

复制代码

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

复制代码

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

复制代码

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

复制代码

 1 var gl = this.gl; 2 var program = this.program; 3 var texture = gl.createTexture(); 4 var uMap = gl.getUniformLocation(program, 'map'); 5  6 gl.activeTexture(gl.TEXTURE0); 7 gl.bindTexture(gl.TEXTURE_2D, texture); 8  9 10 11 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);12 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);13 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);14 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);15 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);16 gl.generateMipmap(gl.TEXTURE_2D);17 18 gl.uniform1i(uMap, 0);                
19 20 // 绘制21 gl.clearColor(0, 0, 0, 1);22 gl.clear(gl.COLOR_BUFFER_BIT);23 gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);

复制代码

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

1 var pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);2 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教程,有兴趣的同学可以关注。

 

四、有无现成类库?

大家可以看到,我实现的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自己定制,更为灵活。
另外,这个组件我没打算深度封装,也没打算维护……嗯,就这样。

 

原文出处

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消