import { Observable , ReplaySubject } from 'rxjs' ; import { assert , prettyPrintBytes } from './auxiliaries' ; /**
 * This register enables monitoring of memory (de)allocations and is intended for use in WebGL context for internal GPU
 * memory allocation tracking. For it, a unique identifier for registration has to be created:
 * ```
 * let gpuAllocReg = this.context.gpuAllocationRegister;
 * const identifier = gpuAllocReg.createUniqueIdentifier('gpu-object');
 * ```
 *
 * Then allocations, deallocations, and reallocations can be registered:
 * ```
 * gpuAllocReg.allocate(identifier, this.sizeofRGBAColorAttachment());
 * gpuAllocReg.allocate(identifier, this.sizeofDepthStencilAttachment());
 * // ... reallocation
 * gpuAllocReg.reallocate(identifier,
 *     this.sizeofRGBAColorAttachment() + this.sizeofDepthStencilAttachment());
 * // ... uninitialize
 * gpuAllocReg.reallocate(identifier, 0);
 * ```
 *
 * Requesting the allocated memory can be done as follows:
 * ```
 * // memory allocated by identifier:
 * console.log(mfCanvas.context.gpuAllocationRegister.toString());
 * //> IntermediateFBO: 10.582MiB, AccumulationPingFBO: 21.163MiB, AccumulationPongFBO: 21.163MiB
 *
 * // memory allocated over all identifiers:
 * console.log(mfCanvas.context.gpuAllocationRegister.bytesToString());
 * //> 52.908MiB
 * ```
 */ export class AllocationRegister { /**
     * Map that provides access to the accumulated memory allocations for all registered identifiers.
     */ protected _bytesByIdentifier = new Map < string , GLsizei > ( ) ; see bytes protected _bytes : GLsizei = 0 ; protected _bytesSubject = new ReplaySubject < [ GLsizei , string ] > ( 1 ) ; /**
     * Utility for communicating this._bytes changes to its associated subject.
     */ protected bytesNext ( ) { this . _bytesSubject . next ( [ this . _bytes , this . bytesToString ( ) ] ) ; } param identifier protected assertIdentifier ( identifier ) { assert ( this . _bytesByIdentifier . has ( identifier ) , `allocation identifier unknown` ) ; } param identifier returns createUniqueIdentifier ( identifier ) { let uniqueIdentifier = identifier ; let unificationSuffix = 2 ; while ( this . _bytesByIdentifier . has ( uniqueIdentifier ) ) { uniqueIdentifier = `${ identifier }-${ unificationSuffix }` ; ++ unificationSuffix ; } this . _bytesByIdentifier . set ( uniqueIdentifier , 0 ) ; return uniqueIdentifier ; } param identifier deleteUniqueIdentifier ( identifier ) { assert ( this . _bytesByIdentifier . has ( identifier ) , `identifier expected to be known for allocation registration` ) ; this . _bytesByIdentifier . delete ( identifier ) ; } param identifier param allocate allocate ( identifier , allocate ) { this . assertIdentifier ( identifier ) ; assert ( allocate >= 0 , `positive number of bytes expected for allocation, given ${ allocate }` ) ; if ( allocate === 0 ) { return ; } const bytes = ( this . _bytesByIdentifier . get ( identifier ) ! ) + allocate ; this . _bytesByIdentifier . set ( identifier , bytes ) ; this . _bytes = this . _bytes + allocate ; this . bytesNext ( ) ; } param identifier param allocate deallocate ( identifier , deallocate ) { this . assertIdentifier ( identifier ) ; const bytes = this . _bytesByIdentifier . get ( identifier ) ! ; assert ( deallocate >= 0 , `positive number of bytes expected for deallocation, given ${ deallocate }` ) ; assert ( deallocate <= bytes , `deallocation cannot exceed previous allocations of ${ bytes }, given ${ deallocate }` ) ; if ( deallocate === 0 ) { return ; } this . _bytesByIdentifier . set ( identifier , bytes - deallocate ) ; this . _bytes = this . _bytes - deallocate ; this . bytesNext ( ) ; } param identifier param allocate reallocate ( identifier , reallocate ) { this . assertIdentifier ( identifier ) ; assert ( reallocate >= 0 , `positive number of bytes expected for reallocation, given ${ reallocate }` ) ; const previousBytes = this . _bytesByIdentifier . get ( identifier ) ! ; if ( previousBytes === reallocate ) { return ; } this . _bytes = this . _bytes - previousBytes ; this . _bytesByIdentifier . set ( identifier , reallocate ) ; this . _bytes = this . _bytes + reallocate ; this . bytesNext ( ) ; } param identifier param allocate allocated ( identifier ? ) { if ( identifier === undefined ) { return this . _bytes ; } this . assertIdentifier ( identifier ) ; return this . _bytesByIdentifier . get ( identifier ) ! ; } returns toString ( ) { const output = new Array < string > ( ) ; this . _bytesByIdentifier . forEach ( ( bytes , identifier ) => { output . push ( `${ identifier }: ${ prettyPrintBytes ( bytes ) }` ) ; } ) ; return output . join ( ', ' ) ; } param identifier returns bytesToString ( identifier ? ) { return prettyPrintBytes ( this . allocated ( identifier ) ) ; } /**
     * Cache for the overall number of allocated bytes (over all identifiers). This should always be the sum of the
     * bytes allocated over each identifier, which can be validated using validate().
     *
     * This property can be observed, e.g., `allocationRegister.bytesObservable.subscribe()`.
     */ get bytes ( ) : GLsizei { return this . _bytes ; } /**
     * Observable that can be used to subscribe to bytes value changes. Yields a 2-tuple of overall allocated bytes as
     * number and pretty printed string.
     */ get bytes$ ( ) : Observable < [ GLsizei , string ] > { return this . _bytesSubject . asObservable ( ) ; } }