yumi-flutter/lib/shared/tools/sc_room_effect_scheduler.dart
2026-05-22 14:19:53 +08:00

164 lines
4.1 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;
int _suppressedCount = 0;
bool get hasActiveHighCostEffects => _pendingHighCostTaskCount > 0;
bool get isSuppressed => _suppressedCount > 0;
void beginSuppressEffects({String reason = 'unknown'}) {
_suppressedCount += 1;
clearDeferredTasks(reason: reason);
clearHighCostTasks(reason: reason);
_log('begin_suppress reason=$reason count=$_suppressedCount');
}
void endSuppressEffects({String reason = 'unknown'}) {
if (_suppressedCount > 0) {
_suppressedCount -= 1;
}
_log('end_suppress reason=$reason count=$_suppressedCount');
if (!isSuppressed) {
_scheduleDeferredDrainIfNeeded();
}
}
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 (isSuppressed) {
_log('drop_suppressed task=$debugLabel');
return;
}
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) {
_log(
'deferred task failed task=${task.debugLabel} id=${task.id} '
'error=$error stack=$stackTrace',
);
}
_scheduleDeferredDrainIfNeeded();
}
void _cancelDeferredDrain() {
_deferredDrainTimer?.cancel();
_deferredDrainTimer = null;
}
void _log(String message) {}
}
class _DeferredRoomEffectTask {
const _DeferredRoomEffectTask({
required this.id,
required this.debugLabel,
required this.action,
});
final int id;
final String debugLabel;
final VoidCallback action;
}