UNPKG

12.9 kBJavaScriptView Raw
1/*!
2 * @pixi-essentials/object-pool - v0.0.3
3 * Compiled Mon, 20 Jul 2020 15:45:20 UTC
4 *
5 * @pixi-essentials/object-pool is licensed under the MIT License.
6 * http://www.opensource.org/licenses/mit-license
7 */
8import { Ticker, UPDATE_PRIORITY } from '@pixi/ticker';
9
10/*! *****************************************************************************
11Copyright (c) Microsoft Corporation.
12
13Permission to use, copy, modify, and/or distribute this software for any
14purpose with or without fee is hereby granted.
15
16THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
17REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
19INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
20LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22PERFORMANCE OF THIS SOFTWARE.
23***************************************************************************** */
24/* global Reflect, Promise */
25
26var extendStatics = function(d, b) {
27 extendStatics = Object.setPrototypeOf ||
28 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
29 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
30 return extendStatics(d, b);
31};
32
33function __extends(d, b) {
34 extendStatics(d, b);
35 function __() { this.constructor = d; }
36 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
37}
38
39/**
40 * Provides the exponential moving average of a sequence.
41 *
42 * Ignored because not directly exposed.
43 *
44 * @internal
45 * @ignore
46 * @class
47 */
48var AverageProvider = /** @class */ (function () {
49 /**
50 * @ignore
51 * @param {number} windowSize - no. of inputs used to calculate window
52 * @param {number} decayRatio - quantifies the weight of previous values (b/w 0 and 1)
53 */
54 function AverageProvider(windowSize, decayRatio) {
55 this._history = new Array(windowSize);
56 this._decayRatio = decayRatio;
57 this._currentIndex = 0;
58 for (var i = 0; i < windowSize; i++) {
59 this._history[i] = 0;
60 }
61 }
62 /**
63 * @ignore
64 * @param {number} input - the next value in the sequence
65 * @returns {number} - the moving average
66 */
67 AverageProvider.prototype.next = function (input) {
68 var _a = this, history = _a._history, decayRatio = _a._decayRatio;
69 var historyLength = history.length;
70 this._currentIndex = this._currentIndex < historyLength - 1 ? this._currentIndex + 1 : 0;
71 history[this._currentIndex] = input;
72 var weightedSum = 0;
73 var weight = 0;
74 for (var i = this._currentIndex + 1; i < historyLength; i++) {
75 weightedSum = (weightedSum + history[i]) * decayRatio;
76 weight = (weight + 1) * decayRatio;
77 }
78 for (var i = 0; i <= this._currentIndex; i++) {
79 weightedSum = (weightedSum + history[i]) * decayRatio;
80 weight = (weight + 1) * decayRatio;
81 }
82 this._average = weightedSum / weight;
83 return this._average;
84 };
85 AverageProvider.prototype.absDev = function () {
86 var errSum = 0;
87 for (var i = 0, j = this._history.length; i < j; i++) {
88 errSum += Math.abs(this._history[i] - this._average);
89 }
90 return errSum / this._history.length;
91 };
92 return AverageProvider;
93}());
94
95/**
96 * `ObjectPool` provides the framework necessary for pooling minus the object instantiation
97 * method. You can use `ObjectPoolFactory` for objects that can be created using a default
98 * constructor.
99 *
100 * @template T
101 * @class
102 * @public
103 */
104var ObjectPool = /** @class */ (function () {
105 /**
106 * @param {IObjectPoolOptions} options
107 */
108 function ObjectPool(options) {
109 var _this = this;
110 if (options === void 0) { options = {}; }
111 this._gcTick = function () {
112 _this._borrowRateAverage = _this._borrowRateAverageProvider.next(_this._borrowRate);
113 _this._marginAverage = _this._marginAverageProvider.next(_this._freeCount - _this._borrowRate);
114 var absDev = _this._borrowRateAverageProvider.absDev();
115 _this._flowRate = 0;
116 _this._borrowRate = 0;
117 _this._returnRate = 0;
118 var poolSize = _this._freeCount;
119 var poolCapacity = _this._freeList.length;
120 // If the pool is small enough, it shouldn't really matter
121 if (poolSize < 128 && _this._borrowRateAverage < 128 && poolCapacity < 128) {
122 return;
123 }
124 // If pool is say, 2x, larger than borrowing rate on average (adjusted for variance/abs-dev), then downsize.
125 var threshold = Math.max(_this._borrowRateAverage * (_this._capacityRatio - 1), _this._reserveCount);
126 if (_this._freeCount > threshold + absDev) {
127 var newCap = threshold + absDev;
128 _this.capacity = Math.min(_this._freeList.length, Math.ceil(newCap));
129 _this._freeCount = _this._freeList.length;
130 }
131 };
132 /**
133 * Supply pool of objects that can be used to immediately lend.
134 *
135 * @member {Array<T>}
136 * @protected
137 */
138 this._freeList = [];
139 /**
140 * Number of objects in the pool. This is less than or equal to `_pool.length`.
141 *
142 * @member {number}
143 * @protected
144 */
145 this._freeCount = 0;
146 this._borrowRate = 0;
147 this._returnRate = 0;
148 this._flowRate = 0;
149 this._borrowRateAverage = 0;
150 this._reserveCount = options.reserve || 0;
151 this._capacityRatio = options.capacityRatio || 1.2;
152 this._decayRatio = options.decayRatio || 0.67;
153 this._marginAverage = 0;
154 this._borrowRateAverageProvider = new AverageProvider(128, this._decayRatio);
155 this._marginAverageProvider = new AverageProvider(128, this._decayRatio);
156 }
157 Object.defineProperty(ObjectPool.prototype, "capacity", {
158 // TODO: Support object destruction. It might not be so good for perf tho.
159 // /**
160 // * Destroys the object before discarding it.
161 // *
162 // * @param {T} object
163 // */
164 // abstract destroyObject(object: T): void;
165 /**
166 * The number of objects that can be stored in the pool without allocating more space.
167 *
168 * @member {number}
169 */
170 get: function () {
171 return this._freeList.length;
172 },
173 set: function (cp) {
174 this._freeList.length = Math.ceil(cp);
175 },
176 enumerable: false,
177 configurable: true
178 });
179 /**
180 * Obtains an instance from this pool.
181 *
182 * @returns {T}
183 */
184 ObjectPool.prototype.allocate = function () {
185 ++this._borrowRate;
186 ++this._flowRate;
187 if (this._freeCount > 0) {
188 return this._freeList[--this._freeCount];
189 }
190 return this.create();
191 };
192 /**
193 * Obtains an array of instances from this pool. This is faster than allocating multiple objects
194 * separately from this pool.
195 *
196 * @param {number | T[]} lengthOrArray - no. of objects to allocate OR the array itself into which
197 * objects are inserted. The amount to allocate is inferred from the array's length.
198 * @returns {T[]} array of allocated objects
199 */
200 ObjectPool.prototype.allocateArray = function (lengthOrArray) {
201 var array;
202 var length;
203 if (Array.isArray(lengthOrArray)) {
204 array = lengthOrArray;
205 length = lengthOrArray.length;
206 }
207 else {
208 length = lengthOrArray;
209 array = new Array(length);
210 }
211 this._borrowRate += length;
212 this._flowRate += length;
213 var filled = 0;
214 // Allocate as many objects from the existing pool
215 if (this._freeCount > 0) {
216 var pool = this._freeList;
217 var poolFilled = Math.min(this._freeCount, length);
218 var poolSize = this._freeCount;
219 for (var i = 0; i < poolFilled; i++) {
220 array[filled] = pool[poolSize - 1];
221 ++filled;
222 --poolSize;
223 }
224 this._freeCount = poolSize;
225 }
226 // Construct the rest of the allocation
227 while (filled < length) {
228 array[filled] = this.create();
229 ++filled;
230 }
231 return array;
232 };
233 /**
234 * Returns the object to the pool.
235 *
236 * @param {T} object
237 */
238 ObjectPool.prototype.release = function (object) {
239 ++this._returnRate;
240 --this._flowRate;
241 if (this._freeCount === this.capacity) {
242 this.capacity *= this._capacityRatio;
243 }
244 this._freeList[this._freeCount] = object;
245 ++this._freeCount;
246 };
247 /**
248 * Releases all of the objects in the passed array. These need not be allocated using `allocateArray`, however.
249 *
250 * @param {T[]} array
251 */
252 ObjectPool.prototype.releaseArray = function (array) {
253 this._returnRate += array.length;
254 this._flowRate -= array.length;
255 if (this._freeCount + array.length > this.capacity) {
256 // Ensure we have enough capacity to insert the release objects
257 this.capacity = Math.max(this.capacity * this._capacityRatio, this._freeCount + array.length);
258 }
259 // Place objects into pool list
260 for (var i = 0, j = array.length; i < j; i++) {
261 this._freeList[this._freeCount] = array[i];
262 ++this._freeCount;
263 }
264 };
265 /**
266 * Preallocates objects so that the pool size is at least `count`.
267 *
268 * @param {number} count
269 */
270 ObjectPool.prototype.reserve = function (count) {
271 this._reserveCount = count;
272 if (this._freeCount < count) {
273 var diff = this._freeCount - count;
274 for (var i = 0; i < diff; i++) {
275 this._freeList[this._freeCount] = this.create();
276 ++this._freeCount;
277 }
278 }
279 };
280 /**
281 * Dereferences objects for the GC to collect and brings the pool size down to `count`.
282 *
283 * @param {number} count
284 */
285 ObjectPool.prototype.limit = function (count) {
286 if (this._freeCount > count) {
287 var oldCapacity = this.capacity;
288 if (oldCapacity > count * this._capacityRatio) {
289 this.capacity = count * this._capacityRatio;
290 }
291 var excessBound = Math.min(this._freeCount, this.capacity);
292 for (var i = count; i < excessBound; i++) {
293 this._freeList[i] = null;
294 }
295 }
296 };
297 /**
298 * Install the GC on the shared ticker.
299 *
300 * @param {Ticker}[ticker=Ticker.shared]
301 */
302 ObjectPool.prototype.startGC = function (ticker) {
303 if (ticker === void 0) { ticker = Ticker.shared; }
304 ticker.add(this._gcTick, null, UPDATE_PRIORITY.UTILITY);
305 };
306 /**
307 * Stops running the GC on the pool.
308 *
309 * @param {Ticker}[ticker=Ticker.shared]
310 */
311 ObjectPool.prototype.stopGC = function (ticker) {
312 if (ticker === void 0) { ticker = Ticker.shared; }
313 ticker.remove(this._gcTick);
314 };
315 return ObjectPool;
316}());
317
318var poolMap = new Map();
319/**
320 * Factory for creating pools of objects with default constructors. It will store the pool of
321 * a given type and reuse it on further builds.
322 *
323 * @class
324 * @public
325 * @example
326 * ```js
327 * import { ObjectPool, ObjectPoolFactory } from 'pixi-object-pool';
328 *
329 * class AABB {}
330 *
331 * const opool: ObjectPool<AABB> = ObjectPoolFactory.build(AABB) as ObjectPool<AABB>;
332 *
333 * const temp = opool.borrowObject();
334 * // do something
335 * opool.returnObject(temp);
336 * ```
337 */
338var ObjectPoolFactory = /** @class */ (function () {
339 function ObjectPoolFactory() {
340 }
341 /**
342 * @param {Class} Type
343 */
344 ObjectPoolFactory.build = function (Type) {
345 var pool = poolMap.get(Type);
346 if (pool) {
347 return pool;
348 }
349 pool = new (/** @class */ (function (_super) {
350 __extends(DefaultObjectPool, _super);
351 function DefaultObjectPool() {
352 return _super !== null && _super.apply(this, arguments) || this;
353 }
354 DefaultObjectPool.prototype.create = function () {
355 return new Type();
356 };
357 return DefaultObjectPool;
358 }(ObjectPool)))();
359 poolMap.set(Type, pool);
360 return pool;
361 };
362 return ObjectPoolFactory;
363}());
364
365export { ObjectPool, ObjectPoolFactory };
366//# sourceMappingURL=pixi-object-pool.mjs.map