Javascript如何實現GPU加速?

騰訊雲加社群發表於2018-07-04

歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~

本文由騰訊Bugly發表於雲+社群專欄

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

CPU與GPU設計目標不同,導致它們之間內部結構差異很大。 CPU需要應對通用場景,內部結構非常複雜。 而GPU往往面向資料型別統一,且相互無依賴的計算。 所以,我們在Web上實現3D場景時,通常使用WebGL利用GPU運算(大量頂點)。 但是,如果只是通用的計算場景呢?比如處理圖片中大量畫素資訊,我們有辦法使用GPU資源嗎?這正是本文要講的,GPU通用計算,簡稱GPGPU。

2. 例項演示:色塊識別

如下圖所示,我們識別圖片中彩虹糖色塊,給糖果新增表情。

img

img

2.1 例項地址(開啟頁面後,依次點選按鈕“使用CPU計算”、“使用GPU計算”)

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

img

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進行色相轉換(防止光線影響識別準確度),其餘步驟均一致。

Javascript如何實現GPU加速?

2.4 使用GPGPU意義

GPU與CPU資料傳輸過程,與GPU實際運算耗時相當,所以使用GPU運算傳輸成本過高,實測在Android中具有較大優勢。

本測試案例是從webAR專案中抽取,需要實時跟蹤使用者攝像頭處理視訊流(256*256),使用GPU計算意義非常大,否則無法實現實時跟蹤。

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

3.1 首先,我們通過一張流程圖,演示原理

img

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),第一時間獲取更多海量技術實踐乾貨哦~

相關文章