import { PerformanceTimer , Task , TaskQueue } from "@here/harp-utils" ; import THREE = require ( "three" ) ; import { TileTaskGroups } from "./MapView" ; import { PerformanceStatistics } from "./Statistics" ; const DEFAULT_MAX_FPS = 60 ; const DEFAULT_PROCESSING_ESTIMATE_TIME = 2 ; const UPDATE_EVENT = { type : "update" } ; export class MapViewTaskScheduler extends THREE . EventDispatcher { private readonly m_taskQueue : TaskQueue ; private m_throttlingEnabled : boolean = false ; constructor ( private m_maxFps = DEFAULT_MAX_FPS ) { super ( ) ; this . m_taskQueue = new TaskQueue ( { groups : [ TileTaskGroups . FETCH_AND_DECODE , TileTaskGroups . CREATE ] , prioSortFn : ( a , b ) => { return a . getPriority ( ) - b . getPriority ( ) ; } } ) ; this . maxFps = m_maxFps ; } set maxFps ( fps ) { this . m_maxFps = fps <= 0 ? DEFAULT_MAX_FPS : fps ; } get maxFps ( ) : number { return this . m_maxFps ; } get taskQueue ( ) { return this . m_taskQueue ; } get throttlingEnabled ( ) : boolean { return this . m_throttlingEnabled === true ; } set throttlingEnabled ( enabled ) { this . m_throttlingEnabled = enabled ; } /**
     * Sends a request to the [[MapView]] to redraw the scene.
     */ requestUpdate ( ) { this . dispatchEvent ( UPDATE_EVENT ) ; } param frameStartTime processPending ( frameStartTime ) { const stats = PerformanceStatistics . instance ; const currentFrameEvent = stats . enabled ? stats . currentFrame : undefined ; let startTime ; if ( stats . enabled ) { startTime = PerformanceTimer . now ( ) ; } this . m_taskQueue . update ( ) ; let numItemsLeft = this . taskQueue . numItemsLeft ( ) ; currentFrameEvent ?. setValue ( "TaskScheduler.numPendingTasks" , numItemsLeft ) ; if ( this . throttlingEnabled ) { let availableTime = this . spaceInFrame ( frameStartTime ) ; availableTime = availableTime > 2 ? availableTime - 2 : availableTime ; currentFrameEvent ?. setValue ( "TaskScheduler.estimatedAvailableTime" , availableTime ) ; let counter = 0 ; while ( availableTime > 0 && numItemsLeft > 0 ) { counter ++ ; function shouldProcess ( task ) { availableTime -= task . estimatedProcessTime ?. ( ) ?? DEFAULT_PROCESSING_ESTIMATE_TIME ; if ( availableTime > 0 || counter === 1 ) { return true ; } return false ; } [ TileTaskGroups . CREATE , TileTaskGroups . FETCH_AND_DECODE ] . forEach ( tag => { if ( this . m_taskQueue . numItemsLeft ( tag ) ) { this . m_taskQueue . processNext ( tag , shouldProcess ) ; } } ) ; numItemsLeft = this . m_taskQueue . numItemsLeft ( ) ; } numItemsLeft = this . m_taskQueue . numItemsLeft ( ) ; if ( numItemsLeft > 0 ) { currentFrameEvent ?. setValue ( "TaskScheduler.pendingTasksNotYetProcessed" , numItemsLeft ) ; this . requestUpdate ( ) ; } } else { this . m_taskQueue . processNext ( TileTaskGroups . CREATE , undefined , this . m_taskQueue . numItemsLeft ( TileTaskGroups . CREATE ) ) ; this . m_taskQueue . processNext ( TileTaskGroups . FETCH_AND_DECODE , undefined , this . m_taskQueue . numItemsLeft ( TileTaskGroups . FETCH_AND_DECODE ) ) ; } if ( stats . enabled ) { currentFrameEvent ?. setValue ( "TaskScheduler.pendingTasksTime" , PerformanceTimer . now ( ) - startTime ! ) ; } } /**
     * Removes all tasks that have been queued.
     */ clearQueuedTasks ( ) { this . m_taskQueue . clear ( ) ; } private spaceInFrame ( frameStartTime ) { const passedTime = ( performance || Date ) . now ( ) - frameStartTime ; return Math . max ( 1000 / this . m_maxFps - passedTime , 0 ) ; } }