AnimationLoopProxy tests (#1184)

This commit is contained in:
Xiaoji Chen 2019-07-26 16:24:33 -07:00 committed by GitHub
parent ad7040c939
commit b1de8b2b6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 176 additions and 84 deletions

View File

@ -3,6 +3,33 @@ import {getPageLoadPromise, getCanvas} from '@luma.gl/webgl';
import {requestAnimationFrame, cancelAnimationFrame} from '@luma.gl/webgl';
import {log, assert} from '../utils';
function initializeCanvas(_self, canvas) {
const eventHandlers = new Map();
canvas.addEventListener = (type, handler) => {
_self.postMessage({command: 'addEventListener', type});
if (!eventHandlers.has(type)) {
eventHandlers.set(type, []);
}
eventHandlers.get(type).push(handler);
};
canvas.removeEventListener = (type, handler) => {
_self.postMessage({command: 'removeEventListener', type});
const handlers = eventHandlers.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler), 1);
}
};
canvas.dispatchEvent = (type, event) => {
const handlers = eventHandlers.get(type);
if (handlers) {
handlers.forEach(handler => handler(event));
}
};
_self.canvas = canvas;
}
export default class AnimationLoopProxy {
// Create the script for the rendering worker.
// @param opts {object} - options to construct an AnimationLoop instance
@ -15,39 +42,12 @@ export default class AnimationLoopProxy {
});
self.canvas = null;
function initializeCanvas(canvas) {
const eventHandlers = new Map();
canvas.addEventListener = (type, handler) => {
self.postMessage({command: 'addEventListener', type});
if (!eventHandlers.has(type)) {
eventHandlers.set(type, []);
}
eventHandlers.get(type).push(handler);
};
canvas.removeEventListener = (type, handler) => {
self.postMessage({command: 'removeEventListener', type});
const handlers = eventHandlers.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler), 1);
}
};
canvas.dispatchEvent = (type, event) => {
const handlers = eventHandlers.get(type);
if (handlers) {
handlers.forEach(handler => handler(event));
}
};
self.canvas = canvas;
}
self.addEventListener('message', evt => {
switch (evt.data.command) {
self.onmessage = evt => {
const message = evt.data;
switch (message.command) {
case 'start':
initializeCanvas(evt.data.opts.canvas);
animationLoop.start(evt.data.opts);
initializeCanvas(self, message.opts.canvas);
animationLoop.start(message.opts);
break;
case 'stop':
@ -55,17 +55,17 @@ export default class AnimationLoopProxy {
break;
case 'resize':
self.canvas.width = evt.data.width;
self.canvas.height = evt.data.height;
self.canvas.width = message.width;
self.canvas.height = message.height;
break;
case 'event':
self.canvas.dispatchEvent(evt.data.type, evt.data.event);
self.canvas.dispatchEvent(message.type, message.event);
break;
default:
}
});
};
};
}
@ -99,8 +99,6 @@ export default class AnimationLoopProxy {
this._running = false;
this._animationFrameId = null;
this._resolveNextFrame = null;
this._nextFramePromise = null;
// bind methods
this._onMessage = this._onMessage.bind(this);
@ -151,8 +149,6 @@ export default class AnimationLoopProxy {
if (this._running) {
cancelAnimationFrame(this._animationFrameId);
this._animationFrameId = null;
this._nextFramePromise = null;
this._resolveNextFrame = null;
this._running = false;
this.props.onFinalize(this);
}
@ -160,15 +156,6 @@ export default class AnimationLoopProxy {
return this;
}
waitForRender() {
if (!this._nextFramePromise) {
this._nextFramePromise = new Promise(resolve => {
this._resolveNextFrame = resolve;
});
}
return this._nextFramePromise;
}
// PRIVATE METHODS
_onMessage(evt) {
@ -210,11 +197,6 @@ export default class AnimationLoopProxy {
_updateFrame() {
this._resizeCanvasDrawingBuffer();
if (this._resolveNextFrame) {
this._resolveNextFrame(this);
this._nextFramePromise = null;
this._resolveNextFrame = null;
}
this._animationFrameId = requestAnimationFrame(this._updateFrame);
}
@ -224,7 +206,7 @@ export default class AnimationLoopProxy {
// Create an offscreen canvas controlling the main canvas
if (!screenCanvas.transferControlToOffscreen) {
log.error('OffscreenCanvas is not available in your browser.')(); // eslint-disable-line
log.error('OffscreenCanvas is not available in your browser.')();
}
const offscreenCanvas = screenCanvas.transferControlToOffscreen();

View File

@ -1,12 +1,105 @@
import {_AnimationLoopProxy} from '@luma.gl/core';
import {AnimationLoop, _AnimationLoopProxy as AnimationLoopProxy} from '@luma.gl/core';
import test from 'tape-catch';
// import {fixture} from 'test/setup';
import {fixture} from 'test/setup';
test('core#AnimationLoopProxy', t => {
// const {gl} = fixture;
t.ok(_AnimationLoopProxy);
function getMockCanvas() {
return {
getContext: () => fixture.gl
};
}
test('core#AnimationLoopProxy.createWorker', t => {
const mockWorkerContext = {};
const animationLoop = new AnimationLoop();
const getWorker = AnimationLoopProxy.createWorker(animationLoop);
t.ok(typeof getWorker === 'function', 'createWorker returns function');
getWorker(mockWorkerContext);
t.ok(typeof mockWorkerContext.onmessage === 'function', 'worker is initialized');
t.end();
});
test('core#AnimationLoopProxy#events', t => {
// create mock worker context
const outboundMessages = [];
const mockWorkerContext = {
postMessage: data => outboundMessages.push(data)
};
// create mock canvas
const mockCanvas = getMockCanvas();
const clickCalled = [];
const onClick = evt => clickCalled.push(evt);
const animationLoop = new AnimationLoop();
AnimationLoopProxy.createWorker(animationLoop)(mockWorkerContext);
mockWorkerContext.onmessage({
data: {
command: 'start',
opts: {canvas: mockCanvas}
}
});
mockCanvas.addEventListener('click', onClick);
t.deepEqual(
outboundMessages.pop(),
{command: 'addEventListener', type: 'click'},
'notifies main thread'
);
mockWorkerContext.onmessage({
data: {
command: 'event',
type: 'click',
event: {id: 1}
}
});
t.deepEqual(clickCalled, [{id: 1}], 'event is handled');
mockWorkerContext.onmessage({
data: {
command: 'event',
type: 'hover',
event: {id: 2}
}
});
t.deepEqual(clickCalled, [{id: 1}], 'event is not handled');
mockCanvas.removeEventListener('click', onClick);
t.deepEqual(
outboundMessages.pop(),
{command: 'removeEventListener', type: 'click'},
'notifies main thread'
);
mockWorkerContext.onmessage({
data: {
command: 'event',
type: 'hover',
event: {id: 3}
}
});
t.deepEqual(clickCalled, [{id: 1}], 'event is not handled');
mockWorkerContext.onmessage({
data: {
command: 'resize',
width: 100,
height: 100
}
});
t.is(mockCanvas.width, 100, 'canvas size is updated');
mockWorkerContext.onmessage({
data: {
command: 'stop'
}
});
// const animationLoop = new _AnimationLoopProxy(gl).start().stop();
// animationLoop.delete();
t.end();
});

View File

@ -0,0 +1 @@
module.exports = require('../../examples/webpack.config.local');

View File

@ -1,14 +1,7 @@
import {_AnimationLoopProxy as AnimationLoopProxy} from '@luma.gl/core';
import createWorker from 'webworkify-webpack';
// Required by webworkify-webpack :(
const worker = createWorker(require.resolve('./worker'));
const worker = new Worker('./worker.js');
const animationLoop = new AnimationLoopProxy(worker);
export default animationLoop;
/* global window */
if (!window.website) {
animationLoop.start();
}
animationLoop.start();

View File

@ -6,8 +6,7 @@
"start-local": "webpack-dev-server --env.local --progress --hot --open -d"
},
"dependencies": {
"luma.gl": "^5.2.0",
"webworkify-webpack": "^2.1.0"
"luma.gl": "^7.1.0"
},
"devDependencies": {
"html-webpack-plugin": "^3.2.0",

View File

@ -1,12 +1,20 @@
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const EXAMPLE_DIR = resolve(__dirname, '../../../../examples')
const CONFIG = {
mode: 'development',
devServer: {
// Static assets
contentBase: resolve(__dirname, '../../core/picking/')
contentBase: resolve(__dirname, 'core/picking/')
},
resolve: {
alias: {
examples: EXAMPLE_DIR
}
},
entry: {
@ -16,5 +24,21 @@ const CONFIG = {
plugins: [new HtmlWebpackPlugin({title: 'Shadowmap'})]
};
const WORKER_CONFIG = {
target: 'webworker',
entry: {
worker: resolve('./worker.js')
},
plugins: []
};
// This line enables bundling against src in this repo rather than installed module
module.exports = env => (env ? require('../../webpack.config.local')(CONFIG)(env) : CONFIG);
module.exports = env => {
const config = env ? require('../../webpack.config.local')(CONFIG)(env) : CONFIG;
const workerConfig = Object.assign({}, config, WORKER_CONFIG);
return [config, workerConfig];
};

View File

@ -1,12 +1,12 @@
import {_AnimationLoopProxy as AnimationLoopProxy} from '@luma.gl/core';
import animationLoop from '../../core/cubemap/app';
// import animationLoop from '../../core/fragment/app';
// import animationLoop from '../../core/instancing/app';
// import animationLoop from '../../core/mandelbrot/app';
// import animationLoop from '../../core/picking/app';
// import animationLoop from '../../core/shadowmap/app';
// import animationLoop from '../../core/transform/app';
// import animationLoop from '../../core/transform-feedback/app';
// import AnimationLoop from 'examples/core/cubemap/app';
// import AnimationLoop from 'examples/core/fragment/app';
// import AnimationLoop from 'examples/core/instancing/app';
// import AnimationLoop from 'examples/core/mandelbrot/app';
// import AnimationLoop from 'examples/core/picking/app';
import AnimationLoop from 'examples/core/shadowmap/app';
// import AnimationLoop from 'examples/core/transform/app';
// import AnimationLoop from 'examples/core/transform-feedback/app';
export default AnimationLoopProxy.createWorker(animationLoop);
export default AnimationLoopProxy.createWorker(new AnimationLoop())(self);