Modify CanvasKit to work in a Web Worker and add a demo of it in action.

In this CL:
- Modify modules/canvaskit/gpu.js to support the use of OffscreenCanvas.
- Add a CanvasKit demos.skia.org demo for CanvasKit in a Web Worker.

Change-Id: I8c26bd94f2aa5b3c09cf149b056b910b0e4cd602
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/304320
Reviewed-by: Elliot Evans <elliotevans@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Nathaniel Nifong <nifong@google.com>
Commit-Queue: Elliot Evans <elliotevans@google.com>
This commit is contained in:
Elliot Evans 2020-07-21 17:46:58 -04:00 committed by Skia Commit-Bot
parent 2e66ade997
commit 683bbe01ff
5 changed files with 123 additions and 1 deletions

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<title>CanvasKit Web Worker Demo</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
canvas {
border: 1px dashed grey;
}
.canvas-container {
float: left;
}
</style>
<body>
<h1>CanvasKit in a Web Worker demo</h1>
<p>NOTE: this demo currently only works in chromium-based browsers, where
<a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas#Browser_compatibility">
Offscreen Canvas
</a>
is supported.
</p>
<div class="canvas-container">
<h2>Normal Canvas</h2>
<canvas id="onscreen-canvas" width=500 height=500></canvas>
<button id="busy-button">Make main thread busy</button>
</div>
<div class="canvas-container">
<h2>Web Worker Canvas</h2>
<canvas id="offscreen-canvas" width=500 height=500></canvas>
</div>
</body>
<script type="text/javascript" src="https://particles.skia.org/static/canvaskit.js"></script>
<script type="text/javascript" src="shared.js"></script>
<script type="text/javascript" src="main.js"></script>

View File

@ -0,0 +1,34 @@
// Set up worker and send it a message with the canvas to draw to.
const offscreenCanvas = document.getElementById('offscreen-canvas').transferControlToOffscreen();
const worker = new Worker('worker.js');
worker.postMessage({ offscreenCanvas }, [offscreenCanvas]);
const canvasKitInitPromise =
CanvasKitInit({locateFile: (file) => 'https://particles.skia.org/static/'+file});
const skottieJsonPromise =
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json')
.then((response) => response.text());
Promise.all([
canvasKitInitPromise,
skottieJsonPromise
]).then(([
CanvasKit,
jsonStr
]) => {
const onscreenCanvas = document.getElementById('onscreen-canvas');
const surface = CanvasKit.MakeWebGLCanvasSurface(onscreenCanvas, null);
if (!surface) {
throw 'Could not make canvas surface';
}
SkottieExample(CanvasKit, surface, jsonStr);
});
document.getElementById('busy-button').addEventListener('click', () => {
const startTime = performance.now();
// This loop runs for 1300ms, emulating computation.
// 1300ms was chosen because it causes a visually obvious lag in the lego loader animation.
while (performance.now() - startTime < 1300) {
}
});

View File

@ -0,0 +1,21 @@
function SkottieExample(CanvasKit, surface, jsonStr, bounds) {
if (!CanvasKit || !jsonStr) {
return;
}
const animation = CanvasKit.MakeAnimation(jsonStr);
const duration = animation.duration() * 1000;
bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
const firstFrame = performance.now();
function drawFrame(skcanvas) {
const now = performance.now();
const seek = ((now - firstFrame) / duration) % 1.0;
animation.seek(seek);
animation.render(skcanvas, bounds);
surface.requestAnimationFrame(drawFrame);
}
surface.requestAnimationFrame(drawFrame);
}

View File

@ -0,0 +1,27 @@
importScripts('https://particles.skia.org/static/canvaskit.js');
importScripts('shared.js');
const transferCanvasToOffscreenPromise =
new Promise((resolve) => addEventListener('message', resolve));
const canvasKitInitPromise =
CanvasKitInit({locateFile: (file) => 'https://particles.skia.org/static/'+file});
const skottieJsonPromise =
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json')
.then((response) => response.text());
Promise.all([
transferCanvasToOffscreenPromise,
canvasKitInitPromise,
skottieJsonPromise
]).then(([
{ data: { offscreenCanvas } },
CanvasKit,
jsonStr
]) => {
const surface = CanvasKit.MakeWebGLCanvasSurface(offscreenCanvas, null);
if (!surface) {
throw 'Could not make canvas surface';
}
SkottieExample(CanvasKit, surface, jsonStr);
});

View File

@ -58,7 +58,9 @@
CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace, attrs) {
colorSpace = colorSpace || null;
var canvas = idOrElement;
if (canvas.tagName !== 'CANVAS') {
var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement;
var isOffscreenCanvas = canvas instanceof OffscreenCanvas;
if (!isHTMLCanvas && !isOffscreenCanvas) {
canvas = document.getElementById(idOrElement);
if (!canvas) {
throw 'Canvas with id ' + idOrElement + ' was not found';