diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`
${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/packages/unsafe-eval/src/bundle.js b/packages/unsafe-eval/src/bundle.js
new file mode 100644
index 0000000..bf02e75
--- /dev/null
+++ b/packages/unsafe-eval/src/bundle.js
@@ -0,0 +1,8 @@
+import { install } from './install';
+
+if (typeof window.PIXI === 'undefined')
+{
+ throw new Error('Global PIXI not found.');
+}
+
+install(window.PIXI);
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/packages/unsafe-eval/src/bundle.js b/packages/unsafe-eval/src/bundle.js
new file mode 100644
index 0000000..bf02e75
--- /dev/null
+++ b/packages/unsafe-eval/src/bundle.js
@@ -0,0 +1,8 @@
+import { install } from './install';
+
+if (typeof window.PIXI === 'undefined')
+{
+ throw new Error('Global PIXI not found.');
+}
+
+install(window.PIXI);
diff --git a/packages/unsafe-eval/src/index.js b/packages/unsafe-eval/src/index.js
new file mode 100644
index 0000000..b3910c7
--- /dev/null
+++ b/packages/unsafe-eval/src/index.js
@@ -0,0 +1 @@
+export * from './install';
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/packages/unsafe-eval/src/bundle.js b/packages/unsafe-eval/src/bundle.js
new file mode 100644
index 0000000..bf02e75
--- /dev/null
+++ b/packages/unsafe-eval/src/bundle.js
@@ -0,0 +1,8 @@
+import { install } from './install';
+
+if (typeof window.PIXI === 'undefined')
+{
+ throw new Error('Global PIXI not found.');
+}
+
+install(window.PIXI);
diff --git a/packages/unsafe-eval/src/index.js b/packages/unsafe-eval/src/index.js
new file mode 100644
index 0000000..b3910c7
--- /dev/null
+++ b/packages/unsafe-eval/src/index.js
@@ -0,0 +1 @@
+export * from './install';
diff --git a/packages/unsafe-eval/src/install.js b/packages/unsafe-eval/src/install.js
new file mode 100644
index 0000000..11758fd
--- /dev/null
+++ b/packages/unsafe-eval/src/install.js
@@ -0,0 +1,51 @@
+import { syncUniforms } from './syncUniforms';
+
+export function install(PIXI)
+{
+ if (!PIXI || !PIXI.systems || !PIXI.systems.ShaderSystem)
+ {
+ throw new Error('Unable to patch ShaderSystem, class not found.');
+ }
+
+ const { ShaderSystem } = PIXI.systems;
+ let proceed = false;
+
+ // Do a quick check to see if the patch is needed
+ // want to make sure we only apply if necessary!
+ try
+ {
+ ShaderSystem.prototype.systemCheck.call(null);
+ proceed = false;
+ }
+ catch (err)
+ {
+ proceed = true;
+ }
+
+ // Only apply if needed
+ if (proceed)
+ {
+ Object.assign(ShaderSystem.prototype,
+ {
+ systemCheck()
+ {
+ // do nothing, don't throw error
+ },
+ syncUniforms(group, glProgram)
+ {
+ const { shader, renderer } = this;
+
+ /* eslint-disable max-len */
+ syncUniforms(
+ group,
+ shader.program.uniformData,
+ glProgram.uniformData,
+ group.uniforms,
+ renderer
+ );
+ /* eslint-disable max-len */
+ },
+ }
+ );
+ }
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/packages/unsafe-eval/src/bundle.js b/packages/unsafe-eval/src/bundle.js
new file mode 100644
index 0000000..bf02e75
--- /dev/null
+++ b/packages/unsafe-eval/src/bundle.js
@@ -0,0 +1,8 @@
+import { install } from './install';
+
+if (typeof window.PIXI === 'undefined')
+{
+ throw new Error('Global PIXI not found.');
+}
+
+install(window.PIXI);
diff --git a/packages/unsafe-eval/src/index.js b/packages/unsafe-eval/src/index.js
new file mode 100644
index 0000000..b3910c7
--- /dev/null
+++ b/packages/unsafe-eval/src/index.js
@@ -0,0 +1 @@
+export * from './install';
diff --git a/packages/unsafe-eval/src/install.js b/packages/unsafe-eval/src/install.js
new file mode 100644
index 0000000..11758fd
--- /dev/null
+++ b/packages/unsafe-eval/src/install.js
@@ -0,0 +1,51 @@
+import { syncUniforms } from './syncUniforms';
+
+export function install(PIXI)
+{
+ if (!PIXI || !PIXI.systems || !PIXI.systems.ShaderSystem)
+ {
+ throw new Error('Unable to patch ShaderSystem, class not found.');
+ }
+
+ const { ShaderSystem } = PIXI.systems;
+ let proceed = false;
+
+ // Do a quick check to see if the patch is needed
+ // want to make sure we only apply if necessary!
+ try
+ {
+ ShaderSystem.prototype.systemCheck.call(null);
+ proceed = false;
+ }
+ catch (err)
+ {
+ proceed = true;
+ }
+
+ // Only apply if needed
+ if (proceed)
+ {
+ Object.assign(ShaderSystem.prototype,
+ {
+ systemCheck()
+ {
+ // do nothing, don't throw error
+ },
+ syncUniforms(group, glProgram)
+ {
+ const { shader, renderer } = this;
+
+ /* eslint-disable max-len */
+ syncUniforms(
+ group,
+ shader.program.uniformData,
+ glProgram.uniformData,
+ group.uniforms,
+ renderer
+ );
+ /* eslint-disable max-len */
+ },
+ }
+ );
+ }
+}
diff --git a/packages/unsafe-eval/src/syncUniforms.js b/packages/unsafe-eval/src/syncUniforms.js
new file mode 100644
index 0000000..0f1f64d
--- /dev/null
+++ b/packages/unsafe-eval/src/syncUniforms.js
@@ -0,0 +1,198 @@
+// cv = CachedValue
+// v = value
+// ud = uniformData
+// uv = uniformValue
+// l = location
+
+/* eslint-disable max-len */
+const GLSL_TO_SINGLE_SETTERS = {
+ float(gl, location, cv, v)
+ {
+ if (cv !== v)
+ {
+ cv.v = v;
+ gl.uniform1f(location, v);
+ }
+ },
+ vec2(gl, location, cv, v)
+ {
+ if (cv[0] !== v[0] || cv[1] !== v[1])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ gl.uniform2f(location, v[0], v[1]);
+ }
+ },
+ vec3(gl, location, cv, v)
+ {
+ if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ cv[2] = v[2];
+
+ gl.uniform3f(location, v[0], v[1], v[2]);
+ }
+ },
+ int(gl, location, cv, value) { gl.uniform1i(location, value); },
+ ivec2(gl, location, cv, value) { gl.uniform2i(location, value[0], value[1]); },
+ ivec3(gl, location, cv, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
+ ivec4(gl, location, cv, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
+
+ bool(gl, location, cv, value) { gl.uniform1i(location, value); },
+ bvec2(gl, location, cv, value) { gl.uniform2i(location, value[0], value[1]); },
+ bvec3(gl, location, cv, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
+ bvec4(gl, location, cv, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
+
+ mat2(gl, location, cv, value) { gl.uniformMatrix2fv(location, false, value); },
+ mat3(gl, location, cv, value) { gl.uniformMatrix3fv(location, false, value); },
+ mat4(gl, location, cv, value) { gl.uniformMatrix4fv(location, false, value); },
+
+ sampler2D(gl, location, cv, value) { gl.uniform1i(location, value); },
+ samplerCube(gl, location, cv, value) { gl.uniform1i(location, value); },
+ sampler2DArray(gl, location, cv, value) { gl.uniform1i(location, value); },
+};
+
+const GLSL_TO_ARRAY_SETTERS = {
+ float(gl, location, cv, value) { gl.uniform1fv(location, value); },
+ vec2(gl, location, cv, value) { gl.uniform2fv(location, value); },
+ vec3(gl, location, cv, value) { gl.uniform3fv(location, value); },
+ vec4(gl, location, cv, value) { gl.uniform4fv(location, value); },
+ int(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ ivec2(gl, location, cv, value) { gl.uniform2iv(location, value); },
+ ivec3(gl, location, cv, value) { gl.uniform3iv(location, value); },
+ ivec4(gl, location, cv, value) { gl.uniform4iv(location, value); },
+ bool(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ bvec2(gl, location, cv, value) { gl.uniform2iv(location, value); },
+ bvec3(gl, location, cv, value) { gl.uniform3iv(location, value); },
+ bvec4(gl, location, cv, value) { gl.uniform4iv(location, value); },
+ sampler2D(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ samplerCube(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ sampler2DArray(gl, location, cv, value) { gl.uniform1iv(location, value); },
+};
+/* eslint-disable max-len */
+
+export function syncUniforms(group, uniformData, ud, uv, renderer)
+{
+ let textureCount = 0;
+ let v = null;
+ let cv = null;
+ const gl = renderer.gl;
+
+ for (const i in group.uniforms)
+ {
+ const data = uniformData[i];
+ const uvi = uv[i];
+ const udi = ud[i];
+ const gu = group.uniforms[i];
+
+ if (!data)
+ {
+ if (gu.group)
+ {
+ renderer.shader.syncUniformGroup(uvi);
+ }
+
+ continue;
+ }
+
+ if (data.type === 'float' && data.size === 1)
+ {
+ if (uvi !== udi.value)
+ {
+ udi.value = uvi;
+ gl.uniform1f(udi.location, uvi);
+ }
+ }
+ /* eslint-disable max-len */
+ else if ((data.type === 'sampler2D' || data.type === 'samplerCube' || data.type === 'sampler2DArray') && data.size === 1 && !data.isArray)
+ /* eslint-disable max-len */
+ {
+ renderer.texture.bind(uvi, textureCount);
+
+ if (udi.value !== textureCount)
+ {
+ udi.value = textureCount;
+ gl.uniform1i(udi.location, textureCount);
+ }
+
+ textureCount++;
+ }
+ else if (data.type === 'mat3' && data.size === 1)
+ {
+ if (gu.a !== undefined)
+ {
+ gl.uniformMatrix3fv(udi.location, false, uvi.toArray(true));
+ }
+ else
+ {
+ gl.uniformMatrix3fv(udi.location, false, uvi);
+ }
+ }
+ else if (data.type === 'vec2' && data.size === 1)
+ {
+ if (gu.x !== undefined)
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v.x || cv[1] !== v.y)
+ {
+ cv[0] = v.x;
+ cv[1] = v.y;
+ gl.uniform2f(udi.location, v.x, v.y);
+ }
+ }
+ else
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v[0] || cv[1] !== v[1])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ gl.uniform2f(udi.location, v[0], v[1]);
+ }
+ }
+ }
+ else if (data.type === 'vec4' && data.size === 1)
+ {
+ if (gu.width !== undefined)
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height)
+ {
+ cv[0] = v.x;
+ cv[1] = v.y;
+ cv[2] = v.width;
+ cv[3] = v.height;
+ gl.uniform4f(udi.location, v.x, v.y, v.width, v.height);
+ }
+ }
+ else
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ cv[2] = v[2];
+ cv[3] = v[3];
+
+ gl.uniform4f(udi.location, v[0], v[1], v[2], v[3]);
+ }
+ }
+ }
+ else
+ {
+ const funcArray = (data.size === 1) ? GLSL_TO_SINGLE_SETTERS : GLSL_TO_ARRAY_SETTERS;
+
+ funcArray[data.type].call(null, gl, udi.location, udi.value, uvi);
+ }
+ }
+}
diff --git a/bundles/pixi.js-legacy/package.json b/bundles/pixi.js-legacy/package.json
index b00cbc6..1f7c86c 100644
--- a/bundles/pixi.js-legacy/package.json
+++ b/bundles/pixi.js-legacy/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi-legacy.js",
"module": "lib/pixi-legacy.es.js",
"bundle": "dist/pixi-legacy.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js-legacy.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
diff --git a/bundles/pixi.js/package.json b/bundles/pixi.js/package.json
index e992233..5900705 100644
--- a/bundles/pixi.js/package.json
+++ b/bundles/pixi.js/package.json
@@ -12,6 +12,10 @@
"main": "lib/pixi.js",
"module": "lib/pixi.es.js",
"bundle": "dist/pixi.js",
+ "bundleOutput": {
+ "footer": "PIXI.useDeprecated();",
+ "name": "PIXI"
+ },
"types": "pixi.js.d.ts",
"homepage": "http://www.pixijs.com/",
"bugs": "https://github.com/pixijs/pixi.js/issues",
@@ -55,6 +59,7 @@
"@pixi/mixin-get-global-position": "^5.0.0-rc.2",
"@pixi/particles": "^5.0.0-rc.2",
"@pixi/prepare": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/sprite": "^5.0.0-rc.2",
"@pixi/sprite-animated": "^5.0.0-rc.2",
diff --git a/bundles/pixi.js/src/index.js b/bundles/pixi.js/src/index.js
index 554ef58..3e016f9 100644
--- a/bundles/pixi.js/src/index.js
+++ b/bundles/pixi.js/src/index.js
@@ -107,6 +107,7 @@
export * from '@pixi/mesh';
export * from '@pixi/mesh-extras';
export * from '@pixi/particles';
+export * from '@pixi/runner';
export * from '@pixi/sprite';
export * from '@pixi/spritesheet';
export * from '@pixi/sprite-animated';
diff --git a/package.json b/package.json
index 92fccaa..6d81947 100644
--- a/package.json
+++ b/package.json
@@ -17,14 +17,14 @@
"lint": "eslint *.js test bundles packages tools --ignore-path .gitignore --max-warnings 0",
"lintfix": "npm run lint -- --fix",
"prebuild": "npm run clean:build",
- "build:min": "lerna run build:min --concurrency 1 --scope pixi*",
+ "build:min": "lerna run build:min --concurrency 1",
"build": "rollup -c",
"postbuild": "npm run types",
"watch": "rollup -cw",
"lerna": "lerna",
"predist": "rimraf dist/*",
"dist": "npm run docs && npm run build:min",
- "postdist": "copyfiles -f \"bundles/*/dist/*\" dist",
+ "postdist": "copyfiles -f \"{bundles,packages}/*/dist/*\" dist",
"prerelease": "npm run clean:build && npm test",
"postversion": "npm run build && npm run build:min",
"release": "lerna publish"
diff --git a/packages/core/package.json b/packages/core/package.json
index 8ec2531..ebb5a92 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -28,12 +28,12 @@
"@pixi/constants": "^5.0.0-rc.2",
"@pixi/display": "^5.0.0-rc.2",
"@pixi/math": "^5.0.0-rc.2",
+ "@pixi/runner": "^5.0.0-rc.2",
"@pixi/settings": "^5.0.0-rc.2",
"@pixi/ticker": "^5.0.0-rc.2",
"@pixi/utils": "^5.0.0-rc.2",
"eventemitter3": "^3.1.0",
- "ismobilejs": "^0.5.1",
- "mini-runner": "^1.0.1"
+ "ismobilejs": "^0.5.1"
},
"devDependencies": {
"floss": "^2.1.5"
diff --git a/packages/core/src/Renderer.js b/packages/core/src/Renderer.js
index 3f59408..bf0b872 100644
--- a/packages/core/src/Renderer.js
+++ b/packages/core/src/Renderer.js
@@ -16,7 +16,7 @@
import { RENDERER_TYPE } from '@pixi/constants';
import UniformGroup from './shader/UniformGroup';
import { Matrix } from '@pixi/math';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* The Renderer draws the scene and all its content onto a WebGL enabled canvas.
@@ -79,20 +79,20 @@
// TODO legacy!
/**
- * Internal signal instances of **mini-runner**, these
+ * Internal signal instances of **runner**, these
* are assigned to each system created.
- * @see https://github.com/GoodBoyDigital/mini-runner
+ * @see PIXI.Runner
* @name PIXI.Renderer#runners
* @private
* @type {object}
* @readonly
- * @property {Runner} destroy - Destroy runner
- * @property {Runner} contextChange - Context change runner
- * @property {Runner} reset - Reset runner
- * @property {Runner} update - Update runner
- * @property {Runner} postrender - Post-render runner
- * @property {Runner} prerender - Pre-render runner
- * @property {Runner} resize - Resize runner
+ * @property {PIXI.Runner} destroy - Destroy runner
+ * @property {PIXI.Runner} contextChange - Context change runner
+ * @property {PIXI.Runner} reset - Reset runner
+ * @property {PIXI.Runner} update - Update runner
+ * @property {PIXI.Runner} postrender - Post-render runner
+ * @property {PIXI.Runner} prerender - Pre-render runner
+ * @property {PIXI.Runner} resize - Resize runner
*/
this.runners = {
destroy: new Runner('destroy'),
diff --git a/packages/core/src/framebuffer/Framebuffer.js b/packages/core/src/framebuffer/Framebuffer.js
index 786992e..c6aa68a 100644
--- a/packages/core/src/framebuffer/Framebuffer.js
+++ b/packages/core/src/framebuffer/Framebuffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
import Texture from '../textures/BaseTexture';
import DepthResource from '../textures/resources/DepthResource';
import { FORMATS, TYPES } from '@pixi/constants';
diff --git a/packages/core/src/geometry/Buffer.js b/packages/core/src/geometry/Buffer.js
index dd0e43c..5ed6046 100644
--- a/packages/core/src/geometry/Buffer.js
+++ b/packages/core/src/geometry/Buffer.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
let UID = 0;
/* eslint-disable max-len */
diff --git a/packages/core/src/geometry/Geometry.js b/packages/core/src/geometry/Geometry.js
index 5e5bc73..22ba14c 100644
--- a/packages/core/src/geometry/Geometry.js
+++ b/packages/core/src/geometry/Geometry.js
@@ -2,7 +2,7 @@
import Buffer from './Buffer';
import interleaveTypedArrays from './utils/interleaveTypedArrays';
import getBufferType from './utils/getBufferType';
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 };
let UID = 0;
diff --git a/packages/core/src/shader/ShaderSystem.js b/packages/core/src/shader/ShaderSystem.js
index e95450f..d555d2f 100644
--- a/packages/core/src/shader/ShaderSystem.js
+++ b/packages/core/src/shader/ShaderSystem.js
@@ -1,6 +1,7 @@
import System from '../System';
import GLProgram from './GLProgram';
import { generateUniformsSync,
+ unsafeEvalSupported,
defaultValue,
compileProgram } from './utils';
@@ -22,6 +23,9 @@
{
super(renderer);
+ // Validation check that this environment support `new Function`
+ this.systemCheck();
+
/**
* The current WebGL rendering context
*
@@ -42,6 +46,21 @@
this.id = UID++;
}
+ /**
+ * Overrideable function by `@pixi/unsafe-eval` to silence
+ * throwing an error if platform doesn't support unsafe-evals.
+ *
+ * @private
+ */
+ systemCheck()
+ {
+ if (!unsafeEvalSupported())
+ {
+ throw new Error('Current environment does not allow unsafe-eval, '
+ + 'please use @pixi/unsafe-eval module to enable support.');
+ }
+ }
+
contextChange(gl)
{
this.gl = gl;
@@ -98,12 +117,24 @@
if (!group.static || group.dirtyId !== glProgram.uniformGroups[group.id])
{
glProgram.uniformGroups[group.id] = group.dirtyId;
- const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
- syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ this.syncUniforms(group, glProgram);
}
}
+ /**
+ * Overrideable by the @pixi/unsafe-eval package to use static
+ * syncUnforms instead.
+ *
+ * @private
+ */
+ syncUniforms(group, glProgram)
+ {
+ const syncFunc = group.syncUniforms[this.shader.program.id] || this.createSyncGroups(group);
+
+ syncFunc(glProgram.uniformData, group.uniforms, this.renderer);
+ }
+
createSyncGroups(group)
{
const id = this.getSignature(group, this.shader.program.uniformData);
diff --git a/packages/core/src/shader/utils/index.js b/packages/core/src/shader/utils/index.js
index fea970a..047897d 100644
--- a/packages/core/src/shader/utils/index.js
+++ b/packages/core/src/shader/utils/index.js
@@ -6,3 +6,4 @@
export { default as generateUniformsSync } from './generateUniformsSync';
export { default as getTestContext } from './getTestContext';
export { default as checkMaxIfStatementsInShader } from './checkMaxIfStatementsInShader';
+export { default as unsafeEvalSupported } from './unsafeEvalSupported';
diff --git a/packages/core/src/shader/utils/unsafeEvalSupported.js b/packages/core/src/shader/utils/unsafeEvalSupported.js
new file mode 100644
index 0000000..24fc7ff
--- /dev/null
+++ b/packages/core/src/shader/utils/unsafeEvalSupported.js
@@ -0,0 +1,32 @@
+// Cache the result to prevent running this over and over
+let unsafeEval;
+
+/**
+ * Not all platforms allow to generate function code (e.g., `new Function`).
+ * this provides the platform-level detection.
+ *
+ * @private
+ * @returns {boolean}
+ */
+export default function unsafeEvalSupported()
+{
+ if (typeof unsafeEval === 'boolean')
+ {
+ return unsafeEval;
+ }
+
+ try
+ {
+ /* eslint-disable no-new-func */
+ const func = new Function('param1', 'param2', 'param3', 'return param1[param2] === param3;');
+ /* eslint-enable no-new-func */
+
+ unsafeEval = func({ a: 'b' }, 'a', 'b') === true;
+ }
+ catch (e)
+ {
+ unsafeEval = false;
+ }
+
+ return unsafeEval;
+}
diff --git a/packages/core/src/textures/resources/Resource.js b/packages/core/src/textures/resources/Resource.js
index 6faf158..87fc899 100644
--- a/packages/core/src/textures/resources/Resource.js
+++ b/packages/core/src/textures/resources/Resource.js
@@ -1,4 +1,4 @@
-import Runner from 'mini-runner';
+import { Runner } from '@pixi/runner';
/**
* Base resource class for textures that manages validation and uploading, depending on its type.
diff --git a/packages/runner/README.md b/packages/runner/README.md
new file mode 100644
index 0000000..cb81554
--- /dev/null
+++ b/packages/runner/README.md
@@ -0,0 +1,85 @@
+# @pixi/runner
+
+A simple alternative to events and signals with an emphasis on performance.
+
+Can be used as an alternative to events / signals.
+
+## Installation
+
+```bash
+npm install @pixi/runner
+```
+
+## Usage
+
+```js
+import { Runner } from '@pixi/runner';
+
+const onComplete = new Runner('onComplete');
+
+//listenerObject needs to have a 'onComplete' function
+onComplete.add(listenerObject);
+
+//emit and all listeners will have their 'onComplete' functions called
+onComplete.emit(data);
+```
+
+Can be used to execute a funcition on many objects. Handy for games. If you need to update you game elements each frame:
+
+```js
+import { Runner } from '@pixi/runner';
+
+const updateRunner = new Runner('update');
+
+// gameItems should all have a 'update' function
+updateRunner.add(gameItem1);
+updateRunner.add(gameItem2);
+updateRunner.add(gameItem3);
+
+// update game elements..
+updateRunner.emit();
+```
+
+## Features
+
+- Easy to use familiar API.
+- Under the hood it dynamically creates a looping function that is highly optimised.
+- Avoids using 'call' and runs the function directly (which is faster!).
+- You can pass parameters when emitting.
+
+Pros:
+- Doesn't rely on strings.
+- Code-completion works properly.
+- Trying to dispatch or listen to an event type that doesn't exist throws errors (helps you find errors early).
+- No need to create constants to store string values.
+- Easy to identify which signals the object dispatch.
+- Favor composition over inheritance.
+- Doesn't mess with the prototype chain.
+- Its fast, a lot faster than events and signals.
+- Great for when performance matters.
+- Its light weight, with a tiny memory footprint (smaller than events and signals)
+
+
+Cons:
+- Not quite as flexible. All listeners / items in the runner must have the correct function name specified within the runners constructor.
+
+## When to Use
+
+In practice I have found the Runner increadibly useful and so thought it would be nice to share with the world. It currently forms the backbone of the messaging system in our game engine. Its working out great for things like update events, collison events etc.
+
+Great to use if you are say looping through and array and calling the same function on each object. The resulting code is cleaner than a loop whilst still keeping the performance as fast as possible.
+
+So yeah, if you are dispatching signals/events to a lot of listeners often (like everyframe often), then I would considor using this alternative. For most cases, this performace boost is not really important enough to switch from your current fave.
+
+Think of this as a nice alternative for when speed really counts!
+
+to run the tests, move to the runner-benchmark folder then run the following:
+
+```bash
+npm run benchmark
+```
+
+Next open you browser (http://localhost:9966). The test is run in the console.
+The test result above comes from macbook pro chrome 58.
+
+Any thoughts or comments hit me up on twitter [@doormat23](https://twitter.com/doormat23), I'd love to hear them!
diff --git a/packages/runner/benchmark/.gitignore b/packages/runner/benchmark/.gitignore
new file mode 100644
index 0000000..483a9c4
--- /dev/null
+++ b/packages/runner/benchmark/.gitignore
@@ -0,0 +1 @@
+package-lock.json
\ No newline at end of file
diff --git a/packages/runner/benchmark/index.js b/packages/runner/benchmark/index.js
new file mode 100644
index 0000000..5ca4302
--- /dev/null
+++ b/packages/runner/benchmark/index.js
@@ -0,0 +1,121 @@
+/* eslint-disable no-console, func-names */
+const { Runner } = require('../');
+const Signal = require('signals');
+const MiniSignal = require('mini-signals');
+const EventEmitter = require('eventemitter3');
+
+const updateRunner = new Runner('update');
+const updateRunnerAdhoc = new Runner('update');
+const updateSignal = new Signal();
+const updateMiniSignal = new MiniSignal();
+const updateEvent = new EventEmitter();
+
+const numListeners = 10;
+const numCycles = 2000;
+const numRuns = 100;
+const timings = {};
+
+function Listener()
+{
+ this.time = 0;
+}
+
+Listener.prototype.update = function ()
+{
+ this.time++;
+};
+
+for (let i = 0; i < numListeners; i++)
+{
+ const listener = new Listener();
+
+ updateRunner.add(listener);
+ updateRunnerAdhoc.add({ time: 0, update: Listener.prototype.update });
+ updateSignal.add(listener.update, listener);
+ updateMiniSignal.add(listener.update, listener);
+ updateEvent.on('update', listener.update, listener);
+}
+
+// bench helper
+function doBench(name, fn)
+{
+ timings[name] = {
+ runs: [],
+ total: 0,
+ avg: 0,
+ };
+
+ console.log(`\nbenchmarking ${name}...`);
+
+ for (let r = 0; r < numRuns; ++r)
+ {
+ const start = performance.now();
+
+ for (let i = 0; i < numCycles; i++)
+ {
+ fn();
+ }
+
+ let time = performance.now() - start;
+
+ time /= 1000;
+ timings[name].runs.push(time);
+ timings[name].total += time;
+ }
+
+ timings[name].avg = timings[name].total / numRuns;
+
+ console.log(`${name}: ${timings[name].avg}`);
+}
+
+log(`Number of listeners: ${numListeners}`);
+log(`Number of runs each: ${numRuns}`);
+log(`Number of cycles per run: ${numCycles}`);
+
+// ///// SIGNALS ///////
+doBench('signals', function ()
+{
+ updateSignal.dispatch();
+});
+
+// ///// MINI-SIGNALS ///////
+doBench('miniSignals', function ()
+{
+ updateMiniSignal.dispatch();
+});
+
+// ///// EVENTS ///////
+doBench('events', function ()
+{
+ updateEvent.emit('update');
+});
+
+// ////// RUNNER ///////
+doBench('runner', function ()
+{
+ updateRunner.emit();
+});
+
+// ////// RUNNER ADHOC ///////
+doBench('runnerAdHoc', function ()
+{
+ updateRunnerAdhoc.emit();
+});
+
+// ////// RESULTS ///////
+console.log('\n');
+function log(msg)
+{
+ console.log(msg);
+ /* jshint ignore:start */
+ document.write(`${msg}
`);
+ /* jshint ignore:end */
+}
+
+log(`mini-runner is ${timings.signals.avg / timings.runner.avg}x faster than signals`);
+log(`mini-runner is ${timings.miniSignals.avg / timings.runner.avg}x faster than mini-signals`);
+log(`mini-runner is ${timings.events.avg / timings.runner.avg}x faster than events`);
+log('\n');
+log(`mini-runner (adhoc) is ${timings.signals.avg / timings.runnerAdHoc.avg}x faster than signals`);
+log(`mini-runner (adhoc) is ${timings.miniSignals.avg / timings.runnerAdHoc.avg}x faster than mini-signals`);
+log(`mini-runner (adhoc) is ${timings.events.avg / timings.runnerAdHoc.avg}x faster than events`);
diff --git a/packages/runner/benchmark/package.json b/packages/runner/benchmark/package.json
new file mode 100644
index 0000000..06e6cdc
--- /dev/null
+++ b/packages/runner/benchmark/package.json
@@ -0,0 +1,12 @@
+{
+ "private": true,
+ "scripts": {
+ "start": "npm install && budo ."
+ },
+ "devDependencies": {
+ "budo": "^11.6.1",
+ "eventemitter3": "^3.1.0",
+ "mini-signals": "^1.2.0",
+ "signals": "^1.0.0"
+ }
+}
diff --git a/packages/runner/package.json b/packages/runner/package.json
new file mode 100644
index 0000000..9c7ff26
--- /dev/null
+++ b/packages/runner/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@pixi/runner",
+ "version": "5.0.0-rc.2",
+ "main": "lib/runner.js",
+ "module": "lib/runner.es.js",
+ "description": "A simple alternative to events and signals with an emphasis on performance.",
+ "author": "Mat Groves",
+ "contributors": [
+ "Matt Karl "
+ ],
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "keywords": [
+ "runner",
+ "signals",
+ "event",
+ "messaging",
+ "publish",
+ "subscribe",
+ "observer",
+ "pub/sub",
+ "fast"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "floss --path test",
+ "benchmark": "cd benchmark && npm start"
+ },
+ "files": [
+ "lib"
+ ],
+ "devDependencies": {
+ "floss": "^2.1.5"
+ }
+}
diff --git a/packages/runner/src/Runner.js b/packages/runner/src/Runner.js
new file mode 100644
index 0000000..1b5b8fc
--- /dev/null
+++ b/packages/runner/src/Runner.js
@@ -0,0 +1,184 @@
+/**
+ * A Runner is a highly performant and simple alternative to signals. Best used in situations
+ * where events are dispatched to many objects at high frequency (say every frame!)
+ *
+ *
+ * like a signal..
+ * ```
+ * const myObject = {
+ * loaded: new PIXI.Runner('loaded')
+ * }
+ *
+ * const listener = {
+ * loaded: function(){
+ * // thin
+ * }
+ * }
+ *
+ * myObject.update.add(listener);
+ *
+ * myObject.loaded.emit();
+ * ```
+ *
+ * Or for handling calling the same function on many items
+ * ```
+ * const myGame = {
+ * update: new PIXI.Runner('update')
+ * }
+ *
+ * const gameObject = {
+ * update: function(time){
+ * // update my gamey state
+ * }
+ * }
+ *
+ * myGame.update.add(gameObject1);
+ *
+ * myGame.update.emit(time);
+ * ```
+ * @class
+ * @memberof PIXI
+ */
+export default class Runner
+{
+ /**
+ * @param {string} name the function name that will be executed on the listeners added to this Runner.
+ */
+ constructor(name)
+ {
+ this.items = [];
+ this._name = name;
+ }
+
+ /**
+ * Dispatch/Broadcast Runner to all listeners added to the queue.
+ * @param {...any} params - optional parameters to pass to each listener
+ */
+ emit(a0, a1, a2, a3, a4, a5, a6, a7)
+ {
+ if (arguments.length > 8)
+ {
+ throw new Error('max arguments reached');
+ }
+
+ const { name, items } = this;
+
+ for (let i = 0, len = items.length; i < len; i++)
+ {
+ items[i][name](a0, a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a listener to the Runner
+ *
+ * Runners do not need to have scope or functions passed to them.
+ * All that is required is to pass the listening object and ensure that it has contains a function that has the same name
+ * as the name provided to the Runner when it was created.
+ *
+ * Eg A listener passed to this Runner will require a 'complete' function.
+ *
+ * ```
+ * const complete = new PIXI.Runner('complete');
+ * ```
+ *
+ * The scope used will be the object itself.
+ *
+ * @param {any} item - The object that will be listening.
+ */
+ add(item)
+ {
+ if (item[this._name])
+ {
+ this.remove(item);
+ this.items.push(item);
+ }
+
+ return this;
+ }
+
+ /**
+ * Remove a single listener from the dispatch queue.
+ * @param {any} item - The listenr that you would like to remove.
+ */
+ remove(item)
+ {
+ const index = this.items.indexOf(item);
+
+ if (index !== -1)
+ {
+ this.items.splice(index, 1);
+ }
+
+ return this;
+ }
+
+ /**
+ * Check to see if the listener is already in the Runner
+ * @param {any} item - The listener that you would like to check.
+ */
+ contains(item)
+ {
+ return this.items.indexOf(item) !== -1;
+ }
+
+ /**
+ * Remove all listeners from the Runner
+ */
+ removeAll()
+ {
+ this.items.length = 0;
+
+ return this;
+ }
+
+ /**
+ * Remove all references, don't use after this.
+ */
+ destroy()
+ {
+ this.removeAll();
+ this.items = null;
+ this._name = null;
+ }
+
+ /**
+ * `true` if there are no this Runner contains no listeners
+ *
+ * @member {boolean}
+ * @readonly
+ */
+ get empty()
+ {
+ return this.items.length === 0;
+ }
+
+ /**
+ * The name of the runner.
+ *
+ * @member {string}
+ * @readonly
+ */
+ get name()
+ {
+ return this._name;
+ }
+}
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method dispatch
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.dispatch = Runner.prototype.emit;
+
+/**
+ * Alias for `emit`
+ * @memberof PIXI.Runner#
+ * @method run
+ * @see PIXI.Runner#emit
+ */
+Runner.prototype.run = Runner.prototype.emit;
diff --git a/packages/runner/src/index.js b/packages/runner/src/index.js
new file mode 100644
index 0000000..2bc89c3
--- /dev/null
+++ b/packages/runner/src/index.js
@@ -0,0 +1 @@
+export { default as Runner } from './Runner';
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 0000000..2094b04
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,12 @@
+{
+ "globals": {
+ "expect": false,
+ "assert": false,
+ "sinon": false,
+ "PIXI": false
+ },
+ "rules": {
+ "func-names": 0,
+ "no-unused-expressions": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/runner/test/index.js b/packages/runner/test/index.js
new file mode 100644
index 0000000..fcc7f86
--- /dev/null
+++ b/packages/runner/test/index.js
@@ -0,0 +1,131 @@
+const { Runner } = require('../');
+
+describe('PIXI.Runner', function ()
+{
+ it('should should exist', function ()
+ {
+ expect(Runner).to.be.defined;
+ expect(typeof Runner).to.equal('function');
+ });
+
+ it('should implement emit', function ()
+ {
+ const complete = new Runner('complete');
+
+ expect(complete.name).to.equal('complete');
+ const callback = sinon.spy();
+
+ complete.add({ complete: callback });
+ complete.emit();
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ complete.emit();
+ expect(callback.calledTwice).to.be.true;
+ complete.emit();
+ expect(callback.calledThrice).to.be.true;
+ complete.destroy();
+ expect(!complete.items).to.be.true;
+ expect(!complete.name).to.be.true;
+ });
+
+ it('should implement emit with arguments', function ()
+ {
+ const update = new Runner('update');
+ const callback = sinon.spy(function (time, delta)
+ {
+ let len = 0;
+ // Count the number of non-undefined arguments
+
+ for (let i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] !== undefined)
+ {
+ len++;
+ }
+ }
+ expect(len).to.equal(2);
+ expect(time).to.equal(1);
+ expect(delta).to.equal(2);
+ });
+
+ update.add({ update: callback });
+ update.emit(1, 2);
+ expect(callback.called).to.be.true;
+ expect(callback.calledOnce).to.be.true;
+ });
+
+ it('should throw an error with too many arguments', function ()
+ {
+ const complete = new Runner('complete');
+
+ complete.add({
+ // eslint-disable-next-line no-unused-vars, no-empty-function
+ complete(a, b, c, d, e, f, g, h, i) {},
+ });
+ try
+ {
+ complete.emit(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ throw new Error('failed too many args');
+ }
+ catch (e)
+ {
+ expect(!!e).to.equal.true;
+ expect(e.message).to.equal('max arguments reached');
+ }
+ });
+
+ it('should implement multiple targets', function ()
+ {
+ const complete = new Runner('complete');
+ const obj = { complete: sinon.spy() };
+ const obj2 = { complete: sinon.spy() };
+
+ expect(complete.empty).to.be.true;
+ complete.add(obj);
+ expect(complete.contains(obj)).to.be.true;
+ complete.add(obj2);
+ expect(complete.contains(obj2)).to.be.true;
+ complete.emit();
+ expect(!complete.empty).to.be.true;
+ expect(complete.items.length).to.equal(2);
+ expect(obj.complete.called).to.be.true;
+ expect(obj.complete.calledOnce).to.be.true;
+ expect(obj2.complete.called).to.be.true;
+ expect(obj2.complete.calledOnce).to.be.true;
+ complete.remove(obj);
+ expect(complete.items.length).to.equal(1);
+ complete.remove(obj2);
+ expect(complete.items.length).to.equal(0);
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should implement removeAll', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+ // eslint-disable-next-line no-empty-function
+ const obj2 = { complete() {} };
+ const obj3 = {};
+
+ complete
+ .add(obj)
+ .add(obj2)
+ .add(obj3);
+
+ expect(complete.items.length).to.equal(2);
+
+ complete.removeAll();
+ expect(complete.empty).to.be.true;
+ });
+
+ it('should not add items more than once', function ()
+ {
+ const complete = new Runner('complete');
+ // eslint-disable-next-line no-empty-function
+ const obj = { complete() {} };
+
+ complete.add(obj).add(obj);
+ expect(complete.items.length).to.equal(1);
+ });
+});
diff --git a/packages/unsafe-eval/LICENSE b/packages/unsafe-eval/LICENSE
new file mode 100644
index 0000000..6eb6388
--- /dev/null
+++ b/packages/unsafe-eval/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2013-2019 Mathew Groves, Chad Engler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/packages/unsafe-eval/README.md b/packages/unsafe-eval/README.md
new file mode 100644
index 0000000..1d32907
--- /dev/null
+++ b/packages/unsafe-eval/README.md
@@ -0,0 +1,31 @@
+# @pixi/unsafe-eval
+
+Adds support for environments that disallow support of `new Function`, such as WeChat.
+
+## Installation
+
+```bash
+npm install @pixi/unsafe-eval
+```
+
+## Usage
+
+If you are using a bundler, you need to pass the core bundle into the `install` method. This function takes one arguments, either the global `PIXI` object, or the core.
+
+```js
+import * as PIXI from '@pixi/core';
+import { install } from '@pixi/unsafe-eval';
+
+// Apply the patch to PIXI
+install(PIXI);
+
+// Create the renderer with patch applied
+const renderer = new PIXI.Renderer();
+```
+
+If you are including **unsafe-eval.js** direct, you do not need to do anything else:
+
+```html
+
+
+```
\ No newline at end of file
diff --git a/packages/unsafe-eval/package.json b/packages/unsafe-eval/package.json
new file mode 100644
index 0000000..3ad7234
--- /dev/null
+++ b/packages/unsafe-eval/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@pixi/unsafe-eval",
+ "version": "5.0.0-rc.2",
+ "main": "lib/unsafe-eval.js",
+ "module": "lib/unsafe-eval.es.js",
+ "bundle": "dist/unsafe-eval.js",
+ "bundleInput": "src/bundle.js",
+ "bundleOutput": {
+ "format": "umd"
+ },
+ "description": "Adds support for environments that disallow support of new Function",
+ "author": "Matt Karl ",
+ "homepage": "http://pixijs.com/",
+ "bugs": "https://github.com/pixijs/pixi.js/issues",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/pixijs/pixi.js.git"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "lib/",
+ "dist/"
+ ],
+ "scripts": {
+ "build:min": "terser dist/unsafe-eval.js -c -m -o dist/unsafe-eval.min.js --source-map \"content='dist/unsafe-eval.js.map',includeSources=true,filename='dist/unsafe-eval.min.js.map',url='unsafe-eval.min.js.map'\" --comments \"/^!/\""
+ },
+ "devDependencies": {
+ "terser": "^3.14.1"
+ }
+}
diff --git a/packages/unsafe-eval/src/bundle.js b/packages/unsafe-eval/src/bundle.js
new file mode 100644
index 0000000..bf02e75
--- /dev/null
+++ b/packages/unsafe-eval/src/bundle.js
@@ -0,0 +1,8 @@
+import { install } from './install';
+
+if (typeof window.PIXI === 'undefined')
+{
+ throw new Error('Global PIXI not found.');
+}
+
+install(window.PIXI);
diff --git a/packages/unsafe-eval/src/index.js b/packages/unsafe-eval/src/index.js
new file mode 100644
index 0000000..b3910c7
--- /dev/null
+++ b/packages/unsafe-eval/src/index.js
@@ -0,0 +1 @@
+export * from './install';
diff --git a/packages/unsafe-eval/src/install.js b/packages/unsafe-eval/src/install.js
new file mode 100644
index 0000000..11758fd
--- /dev/null
+++ b/packages/unsafe-eval/src/install.js
@@ -0,0 +1,51 @@
+import { syncUniforms } from './syncUniforms';
+
+export function install(PIXI)
+{
+ if (!PIXI || !PIXI.systems || !PIXI.systems.ShaderSystem)
+ {
+ throw new Error('Unable to patch ShaderSystem, class not found.');
+ }
+
+ const { ShaderSystem } = PIXI.systems;
+ let proceed = false;
+
+ // Do a quick check to see if the patch is needed
+ // want to make sure we only apply if necessary!
+ try
+ {
+ ShaderSystem.prototype.systemCheck.call(null);
+ proceed = false;
+ }
+ catch (err)
+ {
+ proceed = true;
+ }
+
+ // Only apply if needed
+ if (proceed)
+ {
+ Object.assign(ShaderSystem.prototype,
+ {
+ systemCheck()
+ {
+ // do nothing, don't throw error
+ },
+ syncUniforms(group, glProgram)
+ {
+ const { shader, renderer } = this;
+
+ /* eslint-disable max-len */
+ syncUniforms(
+ group,
+ shader.program.uniformData,
+ glProgram.uniformData,
+ group.uniforms,
+ renderer
+ );
+ /* eslint-disable max-len */
+ },
+ }
+ );
+ }
+}
diff --git a/packages/unsafe-eval/src/syncUniforms.js b/packages/unsafe-eval/src/syncUniforms.js
new file mode 100644
index 0000000..0f1f64d
--- /dev/null
+++ b/packages/unsafe-eval/src/syncUniforms.js
@@ -0,0 +1,198 @@
+// cv = CachedValue
+// v = value
+// ud = uniformData
+// uv = uniformValue
+// l = location
+
+/* eslint-disable max-len */
+const GLSL_TO_SINGLE_SETTERS = {
+ float(gl, location, cv, v)
+ {
+ if (cv !== v)
+ {
+ cv.v = v;
+ gl.uniform1f(location, v);
+ }
+ },
+ vec2(gl, location, cv, v)
+ {
+ if (cv[0] !== v[0] || cv[1] !== v[1])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ gl.uniform2f(location, v[0], v[1]);
+ }
+ },
+ vec3(gl, location, cv, v)
+ {
+ if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ cv[2] = v[2];
+
+ gl.uniform3f(location, v[0], v[1], v[2]);
+ }
+ },
+ int(gl, location, cv, value) { gl.uniform1i(location, value); },
+ ivec2(gl, location, cv, value) { gl.uniform2i(location, value[0], value[1]); },
+ ivec3(gl, location, cv, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
+ ivec4(gl, location, cv, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
+
+ bool(gl, location, cv, value) { gl.uniform1i(location, value); },
+ bvec2(gl, location, cv, value) { gl.uniform2i(location, value[0], value[1]); },
+ bvec3(gl, location, cv, value) { gl.uniform3i(location, value[0], value[1], value[2]); },
+ bvec4(gl, location, cv, value) { gl.uniform4i(location, value[0], value[1], value[2], value[3]); },
+
+ mat2(gl, location, cv, value) { gl.uniformMatrix2fv(location, false, value); },
+ mat3(gl, location, cv, value) { gl.uniformMatrix3fv(location, false, value); },
+ mat4(gl, location, cv, value) { gl.uniformMatrix4fv(location, false, value); },
+
+ sampler2D(gl, location, cv, value) { gl.uniform1i(location, value); },
+ samplerCube(gl, location, cv, value) { gl.uniform1i(location, value); },
+ sampler2DArray(gl, location, cv, value) { gl.uniform1i(location, value); },
+};
+
+const GLSL_TO_ARRAY_SETTERS = {
+ float(gl, location, cv, value) { gl.uniform1fv(location, value); },
+ vec2(gl, location, cv, value) { gl.uniform2fv(location, value); },
+ vec3(gl, location, cv, value) { gl.uniform3fv(location, value); },
+ vec4(gl, location, cv, value) { gl.uniform4fv(location, value); },
+ int(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ ivec2(gl, location, cv, value) { gl.uniform2iv(location, value); },
+ ivec3(gl, location, cv, value) { gl.uniform3iv(location, value); },
+ ivec4(gl, location, cv, value) { gl.uniform4iv(location, value); },
+ bool(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ bvec2(gl, location, cv, value) { gl.uniform2iv(location, value); },
+ bvec3(gl, location, cv, value) { gl.uniform3iv(location, value); },
+ bvec4(gl, location, cv, value) { gl.uniform4iv(location, value); },
+ sampler2D(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ samplerCube(gl, location, cv, value) { gl.uniform1iv(location, value); },
+ sampler2DArray(gl, location, cv, value) { gl.uniform1iv(location, value); },
+};
+/* eslint-disable max-len */
+
+export function syncUniforms(group, uniformData, ud, uv, renderer)
+{
+ let textureCount = 0;
+ let v = null;
+ let cv = null;
+ const gl = renderer.gl;
+
+ for (const i in group.uniforms)
+ {
+ const data = uniformData[i];
+ const uvi = uv[i];
+ const udi = ud[i];
+ const gu = group.uniforms[i];
+
+ if (!data)
+ {
+ if (gu.group)
+ {
+ renderer.shader.syncUniformGroup(uvi);
+ }
+
+ continue;
+ }
+
+ if (data.type === 'float' && data.size === 1)
+ {
+ if (uvi !== udi.value)
+ {
+ udi.value = uvi;
+ gl.uniform1f(udi.location, uvi);
+ }
+ }
+ /* eslint-disable max-len */
+ else if ((data.type === 'sampler2D' || data.type === 'samplerCube' || data.type === 'sampler2DArray') && data.size === 1 && !data.isArray)
+ /* eslint-disable max-len */
+ {
+ renderer.texture.bind(uvi, textureCount);
+
+ if (udi.value !== textureCount)
+ {
+ udi.value = textureCount;
+ gl.uniform1i(udi.location, textureCount);
+ }
+
+ textureCount++;
+ }
+ else if (data.type === 'mat3' && data.size === 1)
+ {
+ if (gu.a !== undefined)
+ {
+ gl.uniformMatrix3fv(udi.location, false, uvi.toArray(true));
+ }
+ else
+ {
+ gl.uniformMatrix3fv(udi.location, false, uvi);
+ }
+ }
+ else if (data.type === 'vec2' && data.size === 1)
+ {
+ if (gu.x !== undefined)
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v.x || cv[1] !== v.y)
+ {
+ cv[0] = v.x;
+ cv[1] = v.y;
+ gl.uniform2f(udi.location, v.x, v.y);
+ }
+ }
+ else
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v[0] || cv[1] !== v[1])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ gl.uniform2f(udi.location, v[0], v[1]);
+ }
+ }
+ }
+ else if (data.type === 'vec4' && data.size === 1)
+ {
+ if (gu.width !== undefined)
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v.x || cv[1] !== v.y || cv[2] !== v.width || cv[3] !== v.height)
+ {
+ cv[0] = v.x;
+ cv[1] = v.y;
+ cv[2] = v.width;
+ cv[3] = v.height;
+ gl.uniform4f(udi.location, v.x, v.y, v.width, v.height);
+ }
+ }
+ else
+ {
+ cv = udi.value;
+ v = uvi;
+
+ if (cv[0] !== v[0] || cv[1] !== v[1] || cv[2] !== v[2] || cv[3] !== v[3])
+ {
+ cv[0] = v[0];
+ cv[1] = v[1];
+ cv[2] = v[2];
+ cv[3] = v[3];
+
+ gl.uniform4f(udi.location, v[0], v[1], v[2], v[3]);
+ }
+ }
+ }
+ else
+ {
+ const funcArray = (data.size === 1) ? GLSL_TO_SINGLE_SETTERS : GLSL_TO_ARRAY_SETTERS;
+
+ funcArray[data.type].call(null, gl, udi.location, udi.value, uvi);
+ }
+ }
+}
diff --git a/rollup.config.js b/rollup.config.js
index 19323aa..69faa86 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -79,7 +79,7 @@
const external = Object.keys(pkg.dependencies || []);
const basePath = path.relative(__dirname, pkg.location);
const input = path.join(basePath, 'src/index.js');
- const { main, module, bundle } = pkg.toJSON();
+ const { main, module, bundle, bundleInput, bundleOutput } = pkg.toJSON();
const freeze = false;
results.push({
@@ -110,16 +110,14 @@
if (bundle)
{
results.push({
- input,
- output: {
+ input: path.join(basePath, bundleInput || 'src/index.js'),
+ output: Object.assign({
banner,
file: path.join(basePath, bundle),
format: 'iife',
- footer: 'PIXI.useDeprecated();',
freeze,
- name: 'PIXI',
sourcemap,
- },
+ }, bundleOutput),
treeshake: false,
plugins,
});