Javascript如何實現GPU加速?

1、什麼是Javascript實現GPU加速?

CPU與GPU設計目標不一樣,致使它們之間內部結構差別很大。
CPU須要應對通用場景,內部結構很是複雜。
而GPU每每面向數據類型統一,且相互無依賴的計算。
因此,咱們在Web上實現3D場景時,一般使用WebGL利用GPU運算(大量頂點)。
可是,若是隻是通用的計算場景呢?好比處理圖片中大量像素信息,咱們有辦法使用GPU資源嗎?這正是本文要講的,GPU通用計算,簡稱GPGPU。javascript

2、實例演示:色塊識別。

以下圖所示,咱們識別圖片中彩虹糖色塊,給糖果添加表情。html

 

2.一、實例地址(打開頁面後,依次點擊按鈕「使用CPU計算」、「使用GPU計算」):
http://tgideas.qq.com/2018/brucewan/gpgpu.htmljava

2.二、運行代碼:git

 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: v
17     }
18 };

  運行次數:262144次github

2.三、測試結論:
實例中,咱們分別使用GPU和CPU進行色相轉換(防止光線影響識別準確度),其他步驟均一致。web

測試平臺 測試結論
PC GPU較CPU優點較少
iOS GPU較CPU優點較少
Android vivoX20(運行10次平均)
CPU:770ms,GPU:270
GPU較CPU快2.85倍
三星S7(運行10次平均)
CPU:982ms,GPU:174ms
GPU較CPU快5.64倍

 

 2.四、使用GPGPU意義:
GPU與CPU數據傳輸過程,與GPU實際運算耗時至關,因此使用GPU運算傳輸成本太高,實測在Android中具備較大優點。
本測試案例是從webAR項目中抽取,須要實時跟蹤用戶攝像頭處理視頻流(256*256),使用GPU計算意義很是大,不然沒法實現實時跟蹤。canvas

 

3、如何實現GPU通用計算?

3.一、首先,咱們經過一張流程圖,演示原理:
ide

3.二、實現:測試

3.2.一、建立頂點着色器,只是傳遞了貼圖座標。this

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.二、建立片元着色器,根據貼圖座標貼圖。

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.三、根據如上着色器代碼,建立程序對象,變量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.四、傳入頂點數據,建立一個面覆蓋整個畫布。

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.五、傳入原始數據,本例中傳入我要處理的圖像數據,做爲貼圖,最終繪製到屏幕。

 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.六、從最終繪製的畫面上,獲取顏色信息做爲最終處理結果數據。

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.七、完整代碼:
http://tgideas.qq.com/2018/brucewan/gpu.js

其實清楚原理後,總體實現比較簡單。
可是對於不瞭解WebGL的同窗來講,理解上有必定難度,我後續準備寫一個系列的WebGL教程,有興趣的同窗能夠關注。

 

4、有無現成類庫?

你們能夠看到,我實現的gpu.js中,並無將javascript轉換成着色器語言(類C),而是用戶直接傳入着色器代碼。可是github上已有將javascript轉換爲着色器語言的庫。
https://github.com/gpujs/gpu.js

爲何我沒有直接使用呢?
一、簡單的使用,2k能夠實現的代碼,不想引入200k的庫;
二、數據輸入輸出能夠由本身靈活控制;
三、着色器語言很簡單,特別只是使用基礎運算邏輯的代碼,不必由庫從Javascript轉換。

沒有WebGL基礎的同窗,建議直接使用https://github.com/gpujs/gpu.js,從本文理解總體邏輯;
有必定基礎的同窗,建議由http://tgideas.qq.com/2018/brucewan/gpu.js本身定製,更爲靈活。另外,這個組件我沒打算深度封裝,也沒打算維護……嗯,就這樣。

相關文章
相關標籤/搜索