fix: crash on background timer expired with Metal renderer
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user