141 lines
3.5 KiB
Dart
141 lines
3.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:collection';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
class SCRoomEffectScheduler {
|
|
static const Duration _deferredDrainInterval = Duration(milliseconds: 60);
|
|
|
|
static final SCRoomEffectScheduler _instance =
|
|
SCRoomEffectScheduler._internal();
|
|
|
|
factory SCRoomEffectScheduler() => _instance;
|
|
|
|
SCRoomEffectScheduler._internal();
|
|
|
|
final Queue<_DeferredRoomEffectTask> _deferredTasks =
|
|
Queue<_DeferredRoomEffectTask>();
|
|
|
|
Timer? _deferredDrainTimer;
|
|
int _pendingHighCostTaskCount = 0;
|
|
int _nextTaskId = 0;
|
|
|
|
bool get hasActiveHighCostEffects => _pendingHighCostTaskCount > 0;
|
|
|
|
void registerHighCostTaskQueued({String? debugLabel}) {
|
|
_pendingHighCostTaskCount += 1;
|
|
_cancelDeferredDrain();
|
|
_log(
|
|
'register_high_cost task=${debugLabel ?? "unknown"} '
|
|
'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}',
|
|
);
|
|
}
|
|
|
|
void completeHighCostTask({String? debugLabel}) {
|
|
if (_pendingHighCostTaskCount > 0) {
|
|
_pendingHighCostTaskCount -= 1;
|
|
}
|
|
_log(
|
|
'complete_high_cost task=${debugLabel ?? "unknown"} '
|
|
'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}',
|
|
);
|
|
_scheduleDeferredDrainIfNeeded();
|
|
}
|
|
|
|
void clearHighCostTasks({String reason = 'unknown'}) {
|
|
if (_pendingHighCostTaskCount == 0) {
|
|
_scheduleDeferredDrainIfNeeded();
|
|
return;
|
|
}
|
|
_pendingHighCostTaskCount = 0;
|
|
_log(
|
|
'clear_high_cost reason=$reason '
|
|
'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}',
|
|
);
|
|
_scheduleDeferredDrainIfNeeded();
|
|
}
|
|
|
|
void scheduleDeferredEffect({
|
|
required String debugLabel,
|
|
required VoidCallback action,
|
|
}) {
|
|
if (!hasActiveHighCostEffects) {
|
|
action();
|
|
return;
|
|
}
|
|
|
|
final task = _DeferredRoomEffectTask(
|
|
id: _nextTaskId++,
|
|
debugLabel: debugLabel,
|
|
action: action,
|
|
);
|
|
_deferredTasks.add(task);
|
|
_log(
|
|
'defer task=${task.debugLabel} id=${task.id} '
|
|
'pending=$_pendingHighCostTaskCount deferred=${_deferredTasks.length}',
|
|
);
|
|
}
|
|
|
|
void clearDeferredTasks({String reason = 'unknown'}) {
|
|
if (_deferredTasks.isEmpty) {
|
|
_cancelDeferredDrain();
|
|
return;
|
|
}
|
|
_deferredTasks.clear();
|
|
_cancelDeferredDrain();
|
|
_log('clear_deferred reason=$reason');
|
|
}
|
|
|
|
void _scheduleDeferredDrainIfNeeded() {
|
|
if (hasActiveHighCostEffects ||
|
|
_deferredTasks.isEmpty ||
|
|
_deferredDrainTimer != null) {
|
|
return;
|
|
}
|
|
_deferredDrainTimer = Timer(_deferredDrainInterval, _drainNextDeferredTask);
|
|
}
|
|
|
|
void _drainNextDeferredTask() {
|
|
_deferredDrainTimer = null;
|
|
if (hasActiveHighCostEffects || _deferredTasks.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
final task = _deferredTasks.removeFirst();
|
|
_log(
|
|
'drain_deferred task=${task.debugLabel} id=${task.id} '
|
|
'remaining=${_deferredTasks.length}',
|
|
);
|
|
try {
|
|
task.action();
|
|
} catch (error, stackTrace) {
|
|
debugPrint(
|
|
'[RoomEffectScheduler] deferred task failed: $error\n$stackTrace',
|
|
);
|
|
}
|
|
|
|
_scheduleDeferredDrainIfNeeded();
|
|
}
|
|
|
|
void _cancelDeferredDrain() {
|
|
_deferredDrainTimer?.cancel();
|
|
_deferredDrainTimer = null;
|
|
}
|
|
|
|
void _log(String message) {
|
|
debugPrint('[RoomEffectScheduler] $message');
|
|
}
|
|
}
|
|
|
|
class _DeferredRoomEffectTask {
|
|
const _DeferredRoomEffectTask({
|
|
required this.id,
|
|
required this.debugLabel,
|
|
required this.action,
|
|
});
|
|
|
|
final int id;
|
|
final String debugLabel;
|
|
final VoidCallback action;
|
|
}
|