UNPKG

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