iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
2
Modern Web

用 Javascript 當個影像魔術師系列 第 21

Day 21 - Canvas 效能調整 - OffscreenCanvas 及 ImageBitmap (下)

OffscreenCanvas

昨天我們成功將 ImageBitmap 送到 worker,今天我們要來解決的是如何在 worker 裡面處理 Canvas 的更新,因為在 worker 是沒辦法獲取 DOM

OffscreenCanvas 提供了一個 Cavnas ,讓使用上並不一定要在可以獲得 DOM 的環境,也就是說在 worker 中也可以使用。所以我們只要將 OffscreenCanvas 傳遞給 worker,就可以在裡面直接完成更新,不需要一定要在主線程裡面更新。那來看一下用法吧。

const offscreen = canvas.transferControlToOffscreen()
worker.postMessage({ offscreen, type: 'init', width: this.width,
height: this.height }, [offscreen])

其實很簡單,直接對我們原本的 Canvas,使用 transferControlToOffscreen 轉換類型,之後直接傳給 worker 使用即可,而因為 OffscreenCanvas 有實作 Transferable 介面,所以記得使用第二個參數,詳情可以看昨天。這樣我們有可以在 worker 中使用的 canvas 了。

但要注意之前提到的,轉移到 worker 的物件是不能繼續在主線程使用的,所以如果在後面不小心使用到的話會出錯唷!

繼續看 worker,這邊就不對程式碼做優化了,當 onmessage 需要監聽的事件多時記得要處理

let canvas
let context
let imageBitmapTmp
let width
let height
let sliderValueTmp
let play
let barrage
const draw = () => {
  context.drawImage(imageBitmapTmp, 0, 0, width, height)
  const pixelData = context.getImageData(0, 0, width, height)
  const result = applyFilters(pixelData, sliderValueTmp)
  context.putImageData(result, 0, 0)
  barrage.draw()
  requestAnimationFrame(draw)
}
onmessage = function(e) {
  if (e.data.type === 'init') {
    canvas = e.data.offscreen
    context = canvas.getContext('2d')
    width = e.data.width
    height = e.data.height
    barrage = new Barrage({ ctx: context, width, height })
  } else if (e.data.type === 'process' && context) {
    const { imageBitmap, sliderValue } = e.data
    imageBitmapTmp = imageBitmap
    sliderValueTmp = sliderValue
    if (!play) {
      play = true
      draw()
    }
  } 

一樣在 onmessage 裡面新增監聽的處理,把一些初始值處理好,接著再開始動作的時候觸發 draw,其實會發現跟原本的流程及程式碼幾乎都一模一樣,只是差別在我們是在 worker 裡面執行,所以原本在主線程裡面會導致卡頓的計算,因為我們直接在 worker 裡面計算及更新到 canvas 的畫面上,會發現畫面不會在卡頓,看一下效果吧!

可以看到在主線程繁忙的時候,拖拉畫面是會卡頓的,而當使用 OffscreenCanvas 之後,畫面雖然還是會卡頓,但是在主線程的拖拉上是不會被影響的。

因為我們在主線程裡面還是需要從 video 裡面獲取影像再傳給 worker,所以這時候如果主線程有一些耗時計算的話,還是會影響影片的更新,但已經把已知最耗時的計算操作及更新留在 worker 裡面去做了,但如果使用方式是 Canvas 自己在 worker 裡面就可以完成的動畫或計算的話,不管主線程是有多少計算,兩邊因為沒溝通的關係是會互不影響的。

接著就是最關心的效率有沒有提升了,在這邊我們都用 fps 做為比較,然後使用三個濾鏡飽和度、暗部,銳利化來做測試,用 100 個影格來作平均,主線程 大約等於 4.9 fps,而使用 OffscreenCanvas 大約等於 5.8 fps,大約增加了快 20% 的效率。而且也不會讓主線程卡頓,讚讚讚。可惜的是現在支援度真的不佳,有看到其他人用在 Three 上面,如果有大量使用 Canvas 的應用記得確認使用者瀏覽器有沒有支援唷!

小結

DEMO

到這邊,我們調整效能的第一步已經完成了,介紹了兩個很新的 OffscreenCanvas以及 ImageBitmap,將計算移動到其他線程,也附帶增加了一些效率與使用者體驗。

接下來開始就到我們的第二步,減少計算所需要的時間囉!明天見~


上一篇
Day 20 - Canvas 效能調整 - OffscreenCanvas 及 ImageBitmap (上)
下一篇
Day 22 - Canvas 效能調整 - Webassembly (上)
系列文
用 Javascript 當個影像魔術師30

尚未有邦友留言

立即登入留言