mirror of
https://github.com/duanhf2012/origin.git
synced 2026-02-03 22:45:13 +08:00
200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
package frametimer
|
||
|
||
import (
|
||
"context"
|
||
"github.com/duanhf2012/origin/v2/event"
|
||
"github.com/duanhf2012/origin/v2/log"
|
||
"github.com/duanhf2012/origin/v2/service"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
const defaultFps = 50
|
||
const defaultSleepInterval = time.Millisecond * 3
|
||
const maxFps = 1000
|
||
const maxMultiple = 5
|
||
|
||
type FrameGroupID uint64
|
||
type FrameTimerID uint64
|
||
type FrameNumber uint64
|
||
|
||
type timerData struct {
|
||
frameNum FrameNumber
|
||
timerID FrameTimerID
|
||
idx int
|
||
cb TimerCB
|
||
tickerFrameNum FrameNumber
|
||
ctx context.Context
|
||
}
|
||
|
||
type FrameTimer struct {
|
||
service.Module
|
||
fps uint32
|
||
oneFrameTime time.Duration
|
||
ticker *time.Ticker
|
||
|
||
mapFrameGroup map[FrameNumber]map[FrameGroupID]struct{}
|
||
mapGroup map[FrameGroupID]*FrameGroup
|
||
globalFrameNum FrameNumber // 当前帧
|
||
|
||
maxTimerID FrameTimerID
|
||
maxGroupID FrameGroupID
|
||
|
||
mapTimer map[FrameTimerID]*timerData
|
||
timerDataPool *sync.Pool
|
||
|
||
locker sync.Mutex
|
||
sleepInterval time.Duration
|
||
}
|
||
|
||
func (ft *FrameTimer) getTimerData(timerID FrameTimerID) *timerData {
|
||
return ft.mapTimer[timerID]
|
||
}
|
||
|
||
func (ft *FrameTimer) addTimerData(timerID FrameTimerID, frameNum FrameNumber, tickerFrameNum FrameNumber, ctx context.Context, cb TimerCB) *timerData {
|
||
td := ft.timerDataPool.Get().(*timerData)
|
||
td.timerID = timerID
|
||
td.frameNum = frameNum
|
||
td.cb = cb
|
||
td.idx = -1
|
||
td.tickerFrameNum = tickerFrameNum
|
||
td.ctx = ctx
|
||
ft.mapTimer[timerID] = td
|
||
return td
|
||
}
|
||
|
||
func (ft *FrameTimer) removeTimerData(timerID FrameTimerID) {
|
||
td := ft.mapTimer[timerID]
|
||
if td == nil {
|
||
return
|
||
}
|
||
|
||
ft.timerDataPool.Put(td)
|
||
}
|
||
|
||
func (ft *FrameTimer) genGroupID() FrameGroupID {
|
||
ft.maxGroupID++
|
||
return ft.maxGroupID
|
||
}
|
||
|
||
func (ft *FrameTimer) genTimerID() FrameTimerID {
|
||
ft.maxTimerID++
|
||
return ft.maxTimerID
|
||
}
|
||
|
||
func (ft *FrameTimer) getGlobalFrameNumber() FrameNumber {
|
||
return ft.globalFrameNum
|
||
}
|
||
|
||
func (ft *FrameTimer) frameTick() {
|
||
preFrameNum := ft.globalFrameNum
|
||
ft.globalFrameNum++
|
||
|
||
ft.locker.Lock()
|
||
defer ft.locker.Unlock()
|
||
for i := preFrameNum; i <= ft.globalFrameNum; i++ {
|
||
mapGroup := ft.mapFrameGroup[i]
|
||
delete(ft.mapFrameGroup, i)
|
||
for groupID := range mapGroup {
|
||
group := ft.mapGroup[groupID]
|
||
if group == nil {
|
||
continue
|
||
}
|
||
|
||
group.tick(ft.globalFrameNum)
|
||
}
|
||
}
|
||
}
|
||
|
||
func (ft *FrameTimer) removeGroup(groupID FrameGroupID, frameNum FrameNumber) {
|
||
delete(ft.mapFrameGroup[frameNum], groupID)
|
||
}
|
||
|
||
func (ft *FrameTimer) refreshGroupMinFrame(groupID FrameGroupID, preFrameNum FrameNumber, newFrameNum FrameNumber) {
|
||
ft.removeGroup(groupID, preFrameNum)
|
||
|
||
mapGroup := ft.mapFrameGroup[newFrameNum]
|
||
if mapGroup == nil {
|
||
mapGroup = make(map[FrameGroupID]struct{}, 6)
|
||
ft.mapFrameGroup[newFrameNum] = mapGroup
|
||
}
|
||
|
||
mapGroup[groupID] = struct{}{}
|
||
}
|
||
|
||
func (ft *FrameTimer) OnInit() error {
|
||
ft.mapFrameGroup = make(map[FrameNumber]map[FrameGroupID]struct{}, 1024)
|
||
ft.mapGroup = make(map[FrameGroupID]*FrameGroup, 1024)
|
||
ft.mapTimer = make(map[FrameTimerID]*timerData, 2048)
|
||
ft.timerDataPool = &sync.Pool{
|
||
New: func() any {
|
||
return &timerData{}
|
||
},
|
||
}
|
||
|
||
if ft.fps == 0 {
|
||
ft.fps = defaultFps
|
||
}
|
||
|
||
ft.GetEventProcessor().RegEventReceiverFunc(event.Sys_Event_FrameTick, ft.GetEventHandler(), func(e event.IEvent) {
|
||
ev := e.(*event.Event)
|
||
td, ok := ev.Data.(*timerData)
|
||
if !ok {
|
||
log.Error("convert *timerData error")
|
||
return
|
||
}
|
||
td.cb(td.ctx, td.timerID)
|
||
event.DeleteEvent(e)
|
||
})
|
||
|
||
ft.oneFrameTime = time.Second / time.Duration(ft.fps)
|
||
ft.ticker = time.NewTicker(ft.oneFrameTime)
|
||
|
||
if ft.sleepInterval == 0 {
|
||
ft.sleepInterval = defaultSleepInterval
|
||
}
|
||
|
||
go func() {
|
||
preTime := time.Now()
|
||
var preFrame FrameNumber
|
||
|
||
for {
|
||
time.Sleep(ft.sleepInterval)
|
||
frameMax := FrameNumber(time.Now().Sub(preTime) / ft.oneFrameTime)
|
||
for i := preFrame + 1; i <= frameMax; i++ {
|
||
ft.frameTick()
|
||
}
|
||
|
||
preFrame = frameMax
|
||
}
|
||
}()
|
||
|
||
return nil
|
||
}
|
||
|
||
// SetFps 设置帧率,越大误差越低。如果有倍数加速需求,可以适当加大fps,以减少误差。默认50fps
|
||
func (ft *FrameTimer) SetFps(fps uint32) {
|
||
if fps > maxFps {
|
||
fps = maxFps
|
||
}
|
||
|
||
ft.fps = fps
|
||
}
|
||
|
||
// SetAccuracyInterval 设置时间间隔精度,在循环中sleep该时间进行判断。实际上因为sleep有误差,所以暂时不使用fps得出。默认为3ms
|
||
func (ft *FrameTimer) SetAccuracyInterval(interval time.Duration) {
|
||
ft.sleepInterval = interval
|
||
}
|
||
|
||
// NewGroup 创建定时器组
|
||
func (ft *FrameTimer) NewGroup() *FrameGroup {
|
||
var group FrameGroup
|
||
group.ft = ft
|
||
group.init()
|
||
|
||
ft.locker.Lock()
|
||
defer ft.locker.Unlock()
|
||
ft.mapGroup[group.groupID] = &group
|
||
return &group
|
||
}
|