fix: crash on background timer expired with Metal renderer

This commit is contained in:
Acaki
2025-09-01 22:51:41 +08:00
parent 4843c046d7
commit 4ab21aed0e
3 changed files with 26 additions and 9 deletions
+2 -1
View File
@@ -23,12 +23,13 @@
@property (nonatomic) id<CAMetalDrawable> _Nullable nextDrawable;
@property (atomic) BOOL isStopping;
@property (nonatomic) BOOL hdrEnabled;
@property (nonatomic, readonly, nonnull) dispatch_semaphore_t inFlightSemaphore;
- (instancetype _Nonnull )initWithMetalDevice:(id<MTLDevice>_Nonnull)device drawablePixelFormat:(MTLPixelFormat)drawablePixelFormat settings:(TemporarySettings* _Nonnull )currentSettings;
- (void)renderFrame:(nonnull Frame *)frame toLayer:(nonnull CAMetalLayer *)layer;
- (void)waitToRenderTo:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(13.0));
- (BOOL)waitToRenderTo:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(13.0));
- (void)drawableResize:(CGSize)drawableSize;
- (void)shutdown;
+13 -4
View File
@@ -138,6 +138,8 @@ CFStringRef __currentColorSpace;
dispatch_semaphore_t _inFlightSemaphore;
}
@synthesize inFlightSemaphore = _inFlightSemaphore;
- (instancetype)initWithMetalDevice:(id<MTLDevice>)device drawablePixelFormat:(MTLPixelFormat)drawablePixelFormat settings:(TemporarySettings* )currentSettings {
self = [super init];
if (self) {
@@ -842,13 +844,20 @@ CFStringRef __currentColorSpace;
}
}
- (void)waitToRenderTo:(nonnull CAMetalLayer *)layer {
- (BOOL)waitToRenderTo:(nonnull CAMetalLayer *)layer {
// Wait to ensure only `MaxFramesInFlight` number of frames are getting processed
// by any stage in the Metal pipeline (CPU, GPU, Metal, Drivers, etc.).
if (!self.isStopping) {
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)); // 100ms
dispatch_semaphore_wait(_inFlightSemaphore, timeout);
if (self.isStopping) {
return NO;
}
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)); // 100ms
if (dispatch_semaphore_wait(_inFlightSemaphore, timeout) != 0) {
Log(LOG_W, @"Timed out waiting for in-flight frame buffer.");
return NO;
}
return YES;
}
- (void)shutdown {
+11 -4
View File
@@ -19,6 +19,7 @@
MetalVideoRenderer *_renderer;
MetricsHandler _metricsHandler;
CADisplayLink *_displayLink;
BOOL _frameSlotAcquired;
}
- (nonnull instancetype)initWithFrame:(CGRect)bounds framerate:(float)framerate settings:(TemporarySettings* )settings metricsHandler:(MetricsHandler)metricsHandler {
@@ -113,17 +114,20 @@
return;
}
// Renderer obtains a nextDrawable, waiting if necessary
if (@available(iOS 13.0, *)) {
[_renderer waitToRenderTo:layer];
_frameSlotAcquired = [_renderer waitToRenderTo:layer];
}
// If we don't have a frame yet, wait on that too
[_frameQueue waitForEnqueue];
if (_frameSlotAcquired) {
[_frameQueue waitForEnqueue];
}
}
/// Draw frame (used by manual loop)
- (void)renderTo:(nonnull CAMetalLayer *)layer {
if (!_frameSlotAcquired) {
return;
}
CFTimeInterval timeout = (1.0f / _framerate) - _renderer.averageGPUTime;
Frame *frame = [_frameQueue dequeueWithTimeout:timeout];
@@ -133,10 +137,13 @@
//if (@available(iOS 13.0, *)) {
[_renderer renderFrame:frame toLayer:layer];
//}
} else {
dispatch_semaphore_signal([_renderer inFlightSemaphore]);
}
} else {
// When paused, we still dequeue frames to prevent accumulation
// but don't render them. Also sleep a bit to reduce CPU usage
dispatch_semaphore_signal([_renderer inFlightSemaphore]);
usleep(100000);
}
}