Skip to content

Commit

Permalink
new pool system improvements
Browse files Browse the repository at this point in the history
- add the option to use a destroy() method when pushed back into the pool (to mimic the legacy pool implementation)
- rename internal import from pool to legacy_pool (internally only)
- migrate tween objects to the new pool implementation
- pass color arguments directly to the colorPool getter in Camera2d
- export createPool directly under pools
  • Loading branch information
obiot committed Aug 13, 2024
1 parent 3e00d76 commit 45a3a77
Show file tree
Hide file tree
Showing 17 changed files with 79 additions and 30 deletions.
14 changes: 7 additions & 7 deletions packages/melonjs/src/camera/camera2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Matrix2d } from "../math/matrix2d.ts";
import { Matrix3d } from "../math/matrix3d.ts";
import { Rect } from "./../geometries/rectangle.ts";
import { renderer } from "./../video/video.js";
import pool from "./../system/pooling.js";
import Renderable from "./../renderable/renderable.js";
import { clamp, toBeCloseTo } from "./../math/math.ts";
import { game } from "../index.js";
Expand All @@ -17,6 +16,7 @@ import {
} from "../system/event.ts";
import { boundsPool } from "./../physics/bounds.ts";
import { colorPool } from "../math/color.ts";
import { tweenPool } from "../tweens/tween.ts";

/**
* @import {Bounds} from "./../physics/bounds.ts";
Expand Down Expand Up @@ -520,9 +520,9 @@ export default class Camera2d extends Renderable {
* });
*/
fadeOut(color, duration = 1000, onComplete) {
this._fadeOut.color = colorPool.get().copy(color);
this._fadeOut.tween = pool
.pull("Tween", this._fadeOut.color)
this._fadeOut.color = colorPool.get(color);
this._fadeOut.tween = tweenPool
.get(this._fadeOut.color)
.to({ alpha: 0.0 }, { duration })
.onComplete(onComplete || null);
this._fadeOut.tween.isPersistent = true;
Expand All @@ -540,11 +540,11 @@ export default class Camera2d extends Renderable {
* me.game.viewport.fadeIn("#FFFFFF", 75);
*/
fadeIn(color, duration = 1000, onComplete) {
this._fadeIn.color = colorPool.get().copy(color);
this._fadeIn.color = colorPool.get(color);
const _alpha = this._fadeIn.color.alpha;
this._fadeIn.color.alpha = 0.0;
this._fadeIn.tween = pool
.pull("Tween", this._fadeIn.color)
this._fadeIn.tween = tweenPool
.get(this._fadeIn.color)
.to({ alpha: _alpha }, { duration })
.onComplete(onComplete || null);
this._fadeIn.tween.isPersistent = true;
Expand Down
3 changes: 1 addition & 2 deletions packages/melonjs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ import Application from "./application/application.js";
import { cache as plugins } from "./plugin/plugin.js";
import save from "./system/save.ts";
import timer from "./system/timer.ts";
import pool from "./system/pooling.js";
import pool from "./system/legacy_pool.js";
import state from "./state/state.js";
import { BOOT, DOM_READY, eventEmitter } from "./system/event.ts";
import { setNocache } from "./loader/loader.js";
Expand Down Expand Up @@ -203,7 +203,6 @@ export function boot() {
pool.register("me.Collectable", Collectable);
pool.register("me.Trigger", Trigger);
pool.register("me.Light2d", Light2d);
pool.register("me.Tween", Tween, true);
pool.register("me.Particle", Particle, true);
pool.register("me.Sprite", Sprite);
pool.register("me.NineSliceSprite", NineSliceSprite);
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/level/tiled/TMXTileMap.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pool from "./../../system/pooling.js";
import pool from "../../system/legacy_pool.js";
import { game } from "../../index.js";
import { checkVersion } from "./../../utils/utils.ts";
import { collision } from "./../../physics/collision.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/particles/emitter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pool from "./../system/pooling.js";
import pool from "../system/legacy_pool.js";
import ParticleEmitterSettings from "./settings.js";
import { randomFloat } from "./../math/math.ts";
import Container from "./../renderable/container.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/physics/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Rect } from "./../geometries/rectangle.ts";
import { Ellipse } from "./../geometries/ellipse.ts";
import { Polygon, polygonPool } from "../geometries/polygon.ts";
import { Bounds, boundsPool } from "./bounds.ts";
import pool from "./../system/pooling.js";
import pool from "../system/legacy_pool.js";
import { collision } from "./collision.js";
import timer from "./../system/timer.ts";
import { clamp } from "./../math/math.ts";
Expand Down
4 changes: 4 additions & 0 deletions packages/melonjs/src/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { matrix3dPool } from "./math/matrix3d";
import { vector2dPool } from "./math/vector2d";
import { vector3dPool } from "./math/vector3d";
import { boundsPool } from "./physics/bounds";
import { tweenPool } from "./tweens/tween";

const pools = {
vector2d: vector2dPool,
Expand All @@ -24,10 +25,13 @@ const pools = {
rectangle: rectPool,
roundedRectangle: roundedRectanglePool,
ellipse: ellipsePool,
tween: tweenPool,
} as const;

type PoolKey = keyof typeof pools;

export const getPool = <K extends PoolKey>(key: K): (typeof pools)[K] => {
return pools[key];
};

export { createPool } from "./system/pool";
2 changes: 1 addition & 1 deletion packages/melonjs/src/renderable/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Renderable from "./renderable.js";
import { createGUID } from "../utils/utils";
import { defer } from "../utils/function";
import { game } from "../index.js";
import pool from "../system/pooling.js";
import pool from "../system/legacy_pool.js";
import state from "../state/state.js";
import Body from "../physics/body.js";
import { CANVAS_ONRESIZE, eventEmitter } from "../system/event.ts";
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/renderable/light2d.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ellipsePool } from "./../geometries/ellipse.ts";
import { colorPool } from "./../math/color.ts";
import pool from "./../system/pooling.js";
import pool from "../system/legacy_pool.js";
import Renderable from "./renderable.js";

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/renderable/renderable.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Rect } from "./../geometries/rectangle.ts";
import pool from "./../system/pooling.js";
import pool from "../system/legacy_pool.js";
import { releaseAllPointerEvents } from "./../input/input.js";
import { clamp } from "./../math/math.ts";
import Body from "./../physics/body.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/renderable/text/bitmaptext.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Color } from "../../math/color.ts";
import pool from "../../system/pooling.js";
import pool from "../../system/legacy_pool.js";
import { getImage, getBinary } from "../../loader/loader.js";
import Renderable from "../renderable.js";
import TextMetrics from "./textmetrics.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/melonjs/src/renderable/text/text.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Color, colorPool } from "../../math/color.ts";
import { renderer as globalRenderer } from "../../video/video.js";
import pool from "../../system/pooling.js";
import pool from "../../system/legacy_pool.js";
import Renderable from "../renderable.js";
import { nextPowerOfTwo } from "../../math/math.ts";
import setContextStyle from "./textstyle.js";
Expand Down
File renamed without changes.
8 changes: 7 additions & 1 deletion packages/melonjs/src/system/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ export interface Pool<T, A extends unknown[]> {
}

type Reset<A extends unknown[]> = ((...args: A) => void) | undefined;
type Destroy = (() => void) | undefined;

export interface CreatePoolOptions<T, A extends unknown[]> {
instance: T;
reset?: Reset<A>;
destroy?: Destroy;
}

export const createPool = <T, A extends unknown[]>(
options: (...args: A) => CreatePoolOptions<T, A>,
): Pool<T, A> => {
const available = new Set<T>();
const instanceResetMethods = new Map<T, Reset<A>>();
const instanceDestroyMethods = new Map<T, Destroy>();
let inUse: number = 0;

return {
Expand All @@ -36,6 +39,8 @@ export const createPool = <T, A extends unknown[]>(
if (available.has(instance)) {
throw new Error("Instance is already in pool.");
}
const destroy = instanceDestroyMethods.get(instance);
destroy?.();
available.add(instance);
inUse--;
},
Expand All @@ -51,8 +56,9 @@ export const createPool = <T, A extends unknown[]>(
inUse++;
return object;
} else {
const { instance, reset } = options(...args);
const { instance, reset, destroy } = options(...args);
instanceResetMethods.set(instance, reset);
instanceDestroyMethods.set(instance, destroy);
inUse++;
return instance;
}
Expand Down
22 changes: 14 additions & 8 deletions packages/melonjs/src/tweens/tween.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import timer from "../system/timer.js";
import { game } from "../index.js";
import { createPool } from "../system/pool.ts";
import { Easing, EasingFunction } from "./easing.js";
import { Interpolation, InterpolationFunction } from "./interpolation.js";
import { eventEmitter, STATE_RESUME } from "../system/event.js";
Expand Down Expand Up @@ -73,14 +74,6 @@ export default class Tween<T extends Record<string, unknown>> {
this.#boundResumeCallback = this._resumeCallback.bind(this);
}

/**
* reset the tween object to default value
* @ignore
*/
onResetEvent(object: T) {
this.setProperties(object);
}

/**
* @ignore
*/
Expand Down Expand Up @@ -456,3 +449,16 @@ export default class Tween<T extends Record<string, unknown>> {
return Interpolation;
}
}

export const tweenPool = createPool(
<T extends Record<string, unknown>>(object: T) => {
const tween = new Tween(object);

return {
instance: tween,
reset(object: T) {
tween.setProperties(object);
},
};
},
);
2 changes: 1 addition & 1 deletion packages/melonjs/src/video/texture/atlas.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Vector2d } from "../../math/vector2d.ts";
import Sprite from "./../../renderable/sprite.js";
import { renderer } from "./../video.js";
import pool from "./../../system/pooling.js";
import pool from "../../system/legacy_pool.js";
import { getImage } from "./../../loader/loader.js";
import { parseTexturePacker } from "./parser/texturepacker.js";
import { parseSpriteSheet } from "./parser/spritesheet.js";
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { expect, test, vi } from "vitest";
import { createPool } from "./pool";
import { createPool } from "../src/pool";

// eslint-disable-next-line @typescript-eslint/no-extraneous-class
class GameObject {}
class GameObject {
destroyCalled = false;
resetCalled = false;
reset() {
this.resetCalled = true;
}
destroy() {
this.destroyCalled = true;
}
}

test("can acquire objects from the pool", () => {
const pool = createPool(() => {
Expand Down Expand Up @@ -31,6 +39,32 @@ test("can release objects from the pool", () => {
expect(pool.used()).toBe(1);
});

test("object reset and destroy are called when getting & releasing object", () => {
const pool = createPool(() => {
const gameObject = new GameObject();
return {
instance: gameObject,
reset: () => {
gameObject.reset();
},
destroy: () => {
gameObject.destroy();
},
};
});
const object1 = pool.get();

expect(object1).toBeInstanceOf(GameObject);
expect(object1.resetCalled).toEqual(false);
expect(object1.destroyCalled).toEqual(false);
pool.release(object1);
expect(object1.destroyCalled).toEqual(true);

const object2 = pool.get();
expect(object2).toBeInstanceOf(GameObject);
expect(object2.resetCalled).toEqual(true);
});

test("can not release instance that already exist in pool", () => {
const pool = createPool(() => {
return { instance: new GameObject() };
Expand Down

0 comments on commit 45a3a77

Please sign in to comment.