2020-07-23 11:00:46 +08:00
module es {
2020-07-12 14:51:20 +08:00
/ * *
2020-07-23 11:00:46 +08:00
* 通 过 使 用 这 个 类 , 您 可 以 直 观 地 找 到 瓶 颈 和 基 本 的 CPU使用情况 。
2020-07-12 14:51:20 +08:00
* /
2020-07-23 11:00:46 +08:00
export class TimeRuler {
/** 最大条数 8 */
public static readonly maxBars = 8 ;
/** */
public static readonly maxSamples = 256 ;
/** 每条的最大嵌套调用 */
public static readonly maxNestCall = 32 ;
/** 条的高度(以像素为单位) */
public static readonly barHeight = 8 ;
/** 最大显示帧 */
public static readonly maxSampleFrames = 4 ;
/** 持续时间(帧数)为采取抓拍日志。 */
public static readonly logSnapDuration = 120 ;
public static readonly barPadding = 2 ;
public static readonly autoAdjustDelay = 30 ;
2020-07-23 19:28:01 +08:00
private static _instance ;
public static get Instance ( ) : TimeRuler {
if ( ! this . _instance )
this . _instance = new TimeRuler ( ) ;
return this . _instance ;
}
2020-07-23 11:00:46 +08:00
private _frameKey = 'frame' ;
private _logKey = 'log' ;
/** 每帧的日志 */
private _logs : FrameLog [ ] ;
/** 当前显示帧计数 */
private sampleFrames : number ;
/** 获取/设置目标样本帧。 */
public targetSampleFrames : number ;
/** 获取/设置计时器标尺宽度。 */
public width : number ;
public enabled : true ;
/** TimerRuler画的位置。 */
private _position : Vector2 ;
/** 上一帧日志 */
private _prevLog : FrameLog ;
/** 当前帧日志 */
private _curLog : FrameLog ;
/** 当前帧数量 */
private frameCount : number ;
/** */
private markers : MarkerInfo [ ] = [ ] ;
/** 秒表用来测量时间。 */
private stopwacth : stopwatch.Stopwatch = new stopwatch . Stopwatch ( ) ;
/** 从标记名映射到标记id的字典。 */
private _markerNameToIdMap : Map < string , number > = new Map < string , number > ( ) ;
/ * *
* 你 想 在 游 戏 开 始 时 调 用 StartFrame更新方法 。
* 当 游 戏 在 固 定 时 间 步 进 模 式 下 运 行 缓 慢 时 , 更 新 会 多 次 调 用 。
* 在 这 种 情 况 下 , 我 们 应 该 忽 略 StartFrame调用 。
* 为 此 , 我 们 只 需 一 直 跟 踪 StartFrame调用的次数 , 直 到 Draw被调用 。
* /
private _updateCount : number ;
/** */
public showLog = false ;
private _frameAdjust : number ;
constructor ( ) {
this . _logs = new Array < FrameLog > ( 2 ) ;
for ( let i = 0 ; i < this . _logs . length ; ++ i )
this . _logs [ i ] = new FrameLog ( ) ;
this . sampleFrames = this . targetSampleFrames = 1 ;
2020-07-23 15:39:18 +08:00
this . width = Core . graphicsDevice . viewport . width * 0.8 ;
es . Core . emitter . addObserver ( CoreEvents . GraphicsDeviceReset , this . onGraphicsDeviceReset , this ) ;
2020-07-23 11:00:46 +08:00
this . onGraphicsDeviceReset ( ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
private onGraphicsDeviceReset() {
let layout = new Layout ( ) ;
this . _position = layout . place ( new Vector2 ( this . width , TimeRuler . barHeight ) , 0 , 0.01 , Alignment . bottomCenter ) . location ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
/ * *
*
* /
public startFrame() {
// 当这个方法被多次调用时,我们跳过重置帧。
let lock = new LockUtils ( this . _frameKey ) ;
lock . lock ( ) . then ( ( ) = > {
this . _updateCount = parseInt ( egret . localStorage . getItem ( this . _frameKey ) , 10 ) ;
if ( isNaN ( this . _updateCount ) )
this . _updateCount = 0 ;
let count = this . _updateCount ;
count += 1 ;
egret . localStorage . setItem ( this . _frameKey , count . toString ( ) ) ;
if ( this . enabled && ( 1 < count && count < TimeRuler . maxSampleFrames ) )
return ;
// 更新当前帧日志。
this . _prevLog = this . _logs [ this . frameCount ++ & 0x1 ] ;
this . _curLog = this . _logs [ this . frameCount & 0x1 ] ;
let endFrameTime = this . stopwacth . getTime ( ) ;
// 更新标记并创建日志。
for ( let barIndex = 0 ; barIndex < this . _prevLog . bars . length ; ++ barIndex ) {
let prevBar = this . _prevLog . bars [ barIndex ] ;
let nextBar = this . _curLog . bars [ barIndex ] ;
// 重新打开在前一帧中没有调用结束标记的标记。
for ( let nest = 0 ; nest < prevBar . nestCount ; ++ nest ) {
let markerIdx = prevBar . markerNests [ nest ] ;
prevBar . markers [ markerIdx ] . endTime = endFrameTime ;
nextBar . markerNests [ nest ] = nest ;
nextBar . markers [ nest ] . markerId = prevBar . markers [ markerIdx ] . markerId ;
nextBar . markers [ nest ] . beginTime = 0 ;
nextBar . markers [ nest ] . endTime = - 1 ;
nextBar . markers [ nest ] . color = prevBar . markers [ markerIdx ] . color ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
// 更新日志标记
for ( let markerIdx = 0 ; markerIdx < prevBar . markCount ; ++ markerIdx ) {
let duration = prevBar . markers [ markerIdx ] . endTime - prevBar . markers [ markerIdx ] . beginTime ;
let markerId = prevBar . markers [ markerIdx ] . markerId ;
let m = this . markers [ markerId ] ;
m . logs [ barIndex ] . color = prevBar . markers [ markerIdx ] . color ;
if ( ! m . logs [ barIndex ] . initialized ) {
m . logs [ barIndex ] . min = duration ;
m . logs [ barIndex ] . max = duration ;
m . logs [ barIndex ] . avg = duration ;
m . logs [ barIndex ] . initialized = true ;
} else {
m . logs [ barIndex ] . min = Math . min ( m . logs [ barIndex ] . min , duration ) ;
m . logs [ barIndex ] . max = Math . min ( m . logs [ barIndex ] . max , duration ) ;
m . logs [ barIndex ] . avg += duration ;
m . logs [ barIndex ] . avg *= 0.5 ;
if ( m . logs [ barIndex ] . samples ++ >= TimeRuler . logSnapDuration ) {
m . logs [ barIndex ] . snapMin = m . logs [ barIndex ] . min ;
m . logs [ barIndex ] . snapMax = m . logs [ barIndex ] . max ;
m . logs [ barIndex ] . snapAvg = m . logs [ barIndex ] . avg ;
m . logs [ barIndex ] . samples = 0 ;
}
2020-07-12 14:51:20 +08:00
}
}
2020-07-23 11:00:46 +08:00
nextBar . markCount = prevBar . nestCount ;
nextBar . nestCount = prevBar . nestCount ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
this . stopwacth . reset ( ) ;
this . stopwacth . start ( ) ;
} ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
/ * *
* 开 始 测 量 时 间 。
* @param markerName
* @param color
* /
public beginMark ( markerName : string , color : number , barIndex : number = 0 ) {
let lock = new LockUtils ( this . _frameKey ) ;
lock . lock ( ) . then ( ( ) = > {
if ( barIndex < 0 || barIndex >= TimeRuler . maxBars )
throw new Error ( "barIndex argument out of range" ) ;
let bar = this . _curLog . bars [ barIndex ] ;
if ( bar . markCount >= TimeRuler . maxSamples ) {
throw new Error ( "exceeded sample count. either set larger number to timeruler.maxsaple or lower sample count" ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
if ( bar . nestCount >= TimeRuler . maxNestCall ) {
throw new Error ( "exceeded nest count. either set larger number to timeruler.maxnestcall or lower nest calls" ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
// 获取注册的标记
let markerId = this . _markerNameToIdMap . get ( markerName ) ;
if ( isNaN ( markerId ) ) {
// 如果此标记未注册,则注册此标记。
markerId = this . markers . length ;
this . _markerNameToIdMap . set ( markerName , markerId ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
bar . markerNests [ bar . nestCount ++ ] = bar . markCount ;
bar . markers [ bar . markCount ] . markerId = markerId ;
bar . markers [ bar . markCount ] . color = color ;
bar . markers [ bar . markCount ] . beginTime = this . stopwacth . getTime ( ) ;
bar . markers [ bar . markCount ] . endTime = - 1 ;
} ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
/ * *
*
* @param markerName
* @param barIndex
* /
public endMark ( markerName : string , barIndex : number = 0 ) {
let lock = new LockUtils ( this . _frameKey ) ;
lock . lock ( ) . then ( ( ) = > {
if ( barIndex < 0 || barIndex >= TimeRuler . maxBars )
throw new Error ( "barIndex argument out of range" ) ;
let bar = this . _curLog . bars [ barIndex ] ;
if ( bar . nestCount <= 0 ) {
throw new Error ( "call beginMark method before calling endMark method" ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
let markerId = this . _markerNameToIdMap . get ( markerName ) ;
if ( isNaN ( markerId ) ) {
throw new Error ( ` Marker ${ markerName } is not registered. Make sure you specifed same name as you used for beginMark method ` ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
let markerIdx = bar . markerNests [ -- bar . nestCount ] ;
if ( bar . markers [ markerIdx ] . markerId != markerId ) {
throw new Error ( "Incorrect call order of beginMark/endMark method. beginMark(A), beginMark(B), endMark(B), endMark(A) But you can't called it like beginMark(A), beginMark(B), endMark(A), endMark(B)." ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
bar . markers [ markerIdx ] . endTime = this . stopwacth . getTime ( ) ;
} ) ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
/ * *
* 获 取 给 定 bar索引和标记名称的平均时间 。
* @param barIndex
* @param markerName
* /
public getAverageTime ( barIndex : number , markerName : string ) {
if ( barIndex < 0 || barIndex >= TimeRuler . maxBars ) {
throw new Error ( "barIndex argument out of range" ) ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
let result = 0 ;
let markerId = this . _markerNameToIdMap . get ( markerName ) ;
if ( markerId ) {
result = this . markers [ markerId ] . logs [ barIndex ] . avg ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
return result ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
/ * *
*
* /
public resetLog() {
let lock = new LockUtils ( this . _logKey ) ;
lock . lock ( ) . then ( ( ) = > {
let count = parseInt ( egret . localStorage . getItem ( this . _logKey ) , 10 ) ;
count += 1 ;
egret . localStorage . setItem ( this . _logKey , count . toString ( ) ) ;
this . markers . forEach ( markerInfo = > {
for ( let i = 0 ; i < markerInfo . logs . length ; ++ i ) {
markerInfo . logs [ i ] . initialized = false ;
markerInfo . logs [ i ] . snapMin = 0 ;
markerInfo . logs [ i ] . snapMax = 0 ;
markerInfo . logs [ i ] . snapAvg = 0 ;
markerInfo . logs [ i ] . min = 0 ;
markerInfo . logs [ i ] . max = 0 ;
markerInfo . logs [ i ] . avg = 0 ;
markerInfo . logs [ i ] . samples = 0 ;
}
} ) ;
} ) ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
public render ( position : Vector2 = this . _position , width : number = this . width ) {
egret . localStorage . setItem ( this . _frameKey , "0" ) ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
if ( ! this . showLog )
return ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
let height = 0 ;
let maxTime = 0 ;
this . _prevLog . bars . forEach ( bar = > {
if ( bar . markCount > 0 ) {
height += TimeRuler . barHeight + TimeRuler . barPadding * 2 ;
maxTime = Math . max ( maxTime , bar . markers [ bar . markCount - 1 ] . endTime ) ;
}
} )
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
const frameSpan = 1 / 60 * 1000 ;
let sampleSpan = this . sampleFrames * frameSpan ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
if ( maxTime > sampleSpan ) {
this . _frameAdjust = Math . max ( 0 , this . _frameAdjust ) + 1 ;
} else {
this . _frameAdjust = Math . min ( 0 , this . _frameAdjust ) - 1 ;
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
if ( Math . max ( this . _frameAdjust ) > TimeRuler . autoAdjustDelay ) {
this . sampleFrames = Math . min ( TimeRuler . maxSampleFrames , this . sampleFrames ) ;
this . sampleFrames = Math . max ( this . targetSampleFrames , ( maxTime / frameSpan ) + 1 ) ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
this . _frameAdjust = 0 ;
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
let msToPs = width / sampleSpan ;
let startY = position . y - ( height - TimeRuler . barHeight ) ;
let y = startY ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
// TODO: draw
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
/ * *
* 日 志 信 息
* /
export class FrameLog {
public bars : MarkerCollection [ ] ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
constructor ( ) {
this . bars = new Array < MarkerCollection > ( TimeRuler . maxBars ) ;
this . bars . fill ( new MarkerCollection ( ) , 0 , TimeRuler . maxBars ) ;
}
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
/ * *
* 标 记 的 集 合
* /
export class MarkerCollection {
public markers : Marker [ ] = new Array < Marker > ( TimeRuler . maxSamples ) ;
public markCount : number = 0 ;
public markerNests : number [ ] = new Array < number > ( TimeRuler . maxNestCall ) ;
public nestCount : number = 0 ;
constructor ( ) {
this . markers . fill ( new Marker ( ) , 0 , TimeRuler . maxSamples ) ;
this . markerNests . fill ( 0 , 0 , TimeRuler . maxNestCall ) ;
}
2020-07-12 14:51:20 +08:00
}
2020-07-23 11:00:46 +08:00
export class Marker {
public markerId : number = 0 ;
public beginTime : number = 0 ;
public endTime : number = 0 ;
public color : number = 0x000000 ;
2020-07-22 20:07:14 +08:00
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
export class MarkerInfo {
public name : string ;
public logs : MarkerLog [ ] = new Array < MarkerLog > ( TimeRuler . maxBars ) ;
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
constructor ( name ) {
this . name = name ;
this . logs . fill ( new MarkerLog ( ) , 0 , TimeRuler . maxBars ) ;
}
}
2020-07-12 14:51:20 +08:00
2020-07-23 11:00:46 +08:00
export class MarkerLog {
public snapMin : number = 0 ;
public snapMax : number = 0 ;
public snapAvg : number = 0 ;
public min : number = 0 ;
public max : number = 0 ;
public avg : number = 0 ;
public samples : number = 0 ;
public color : number = 0x000000 ;
public initialized : boolean = false ;
2020-07-12 14:51:20 +08:00
}
}