import { Ticker , UPDATE_PRIORITY } from '@pixi/ticker' ; import { AverageProvider } from './AverageProvider' ; interface public export interface IObjectPoolOptions { capacityRatio ? : number ; decayRatio ? : number ; reserve ? : number ; } template T class public export abstract class ObjectPool < T > { protected _freeList : Array < T > ; protected _freeCount : number ; protected _reserveCount : number ; protected _borrowRate : number ; protected _returnRate : number ; protected _flowRate : number ; protected _borrowRateAverage : number ; protected _marginAverage : number ; private _capacityRatio : number ; private _decayRatio : number ; private _borrowRateAverageProvider : AverageProvider ; private _marginAverageProvider : AverageProvider ; param { IObjectPoolOptions } options constructor ( options = { } ) { member protected this . _freeList = [ ] ; member protected this . _freeCount = 0 ; this . _borrowRate = 0 ; this . _returnRate = 0 ; this . _flowRate = 0 ; this . _borrowRateAverage = 0 ; this . _reserveCount = options . reserve || 0 ; this . _capacityRatio = options . capacityRatio || 1.2 ; this . _decayRatio = options . decayRatio || 0.67 ; this . _marginAverage = 0 ; this . _borrowRateAverageProvider = new AverageProvider ( 128 , this . _decayRatio ) ; this . _marginAverageProvider = new AverageProvider ( 128 , this . _decayRatio ) ; } abstract returns { T } abstract create ( ) ; member protected get capacity ( ) : number { return this . _freeList . length ; } protected set capacity ( cp ) { this . _freeList . length = Math . ceil ( cp ) ; } returns { T } allocate ( ) { ++ this . _borrowRate ; ++ this . _flowRate ; if ( this . _freeCount > 0 ) { return this . _freeList [ -- this . _freeCount ] ; } return this . create ( ) ; } param { number | T [ ] } lengthOrArray returns { T [ ] } allocateArray ( lengthOrArray ) { let array ; let length ; if ( Array . isArray ( lengthOrArray ) ) { array = lengthOrArray ; length = lengthOrArray . length ; } else { length = lengthOrArray ; array = new Array ( length ) ; } this . _borrowRate += length ; this . _flowRate += length ; let filled = 0 ; if ( this . _freeCount > 0 ) { const pool = this . _freeList ; const poolFilled = Math . min ( this . _freeCount , length ) ; let poolSize = this . _freeCount ; for ( let i = 0 ; i < poolFilled ; i ++ ) { array [ filled ] = pool [ poolSize - 1 ] ; ++ filled ; -- poolSize ; } this . _freeCount = poolSize ; } while ( filled < length ) { array [ filled ] = this . create ( ) ; ++ filled ; } return array ; } param { T } object release ( object ) { ++ this . _returnRate ; -- this . _flowRate ; if ( this . _freeCount === this . capacity ) { this . capacity *= this . _capacityRatio ; } this . _freeList [ this . _freeCount ] = object ; ++ this . _freeCount ; } param { T [ ] } array releaseArray ( array ) { this . _returnRate += array . length ; this . _flowRate -= array . length ; if ( this . _freeCount + array . length > this . capacity ) { this . capacity = Math . max ( this . capacity * this . _capacityRatio , this . _freeCount + array . length ) ; } for ( let i = 0 , j = array . length ; i < j ; i ++ ) { this . _freeList [ this . _freeCount ] = array [ i ] ; ++ this . _freeCount ; } } param { number } count reserve ( count ) { this . _reserveCount = count ; if ( this . _freeCount < count ) { const diff = this . _freeCount - count ; for ( let i = 0 ; i < diff ; i ++ ) { this . _freeList [ this . _freeCount ] = this . create ( ) ; ++ this . _freeCount ; } } } param { number } count limit ( count ) { if ( this . _freeCount > count ) { const oldCapacity = this . capacity ; if ( oldCapacity > count * this . _capacityRatio ) { this . capacity = count * this . _capacityRatio ; } const excessBound = Math . min ( this . _freeCount , this . capacity ) ; for ( let i = count ; i < excessBound ; i ++ ) { this . _freeList [ i ] = null ; } } } param { Ticker } ticker startGC ( ticker = Ticker . shared ) { ticker . add ( this . _gcTick , null , UPDATE_PRIORITY . UTILITY ) ; } param { Ticker } ticker stopGC ( ticker = Ticker . shared ) { ticker . remove ( this . _gcTick ) ; } private _gcTick = ( ) : void => { this . _borrowRateAverage = this . _borrowRateAverageProvider . next ( this . _borrowRate ) ; this . _marginAverage = this . _marginAverageProvider . next ( this . _freeCount - this . _borrowRate ) ; const absDev = this . _borrowRateAverageProvider . absDev ( ) ; this . _flowRate = 0 ; this . _borrowRate = 0 ; this . _returnRate = 0 ; const poolSize = this . _freeCount ; const poolCapacity = this . _freeList . length ; if ( poolSize < 128 && this . _borrowRateAverage < 128 && poolCapacity < 128 ) { return ; } const threshold = Math . max ( this . _borrowRateAverage * ( this . _capacityRatio - 1 ) , this . _reserveCount ) ; if ( this . _freeCount > threshold + absDev ) { const newCap = threshold + absDev ; this . capacity = Math . min ( this . _freeList . length , Math . ceil ( newCap ) ) ; this . _freeCount = this . _freeList . length ; } } ; }