MetalViewController: run a no-op DisplayLink at the target framerate so iOS won't switch to a slower rate

This commit is contained in:
Andy Grundman
2025-08-13 15:16:19 +08:00
committed by Acaki
parent 868288e70f
commit 6010182592
+19
View File
@@ -18,6 +18,7 @@
MetalView *_metalView;
MetalVideoRenderer *_renderer;
MetricsHandler _metricsHandler;
CADisplayLink *_displayLink;
}
- (nonnull instancetype)initWithFrame:(CGRect)bounds framerate:(float)framerate enableHdr:(BOOL)enableHdr metricsHandler:(MetricsHandler)metricsHandler {
@@ -72,6 +73,20 @@
// Initialize the renderer-dependent view properties.
view.metalLayer.pixelFormat = renderer.colorPixelFormat;
view.metalLayer.maximumDrawableCount = 3;
// We need a no-op displaylink timer or iOS can decide to run at 60fps
// The overhead from this should be minimal.
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkHandler:)];
if (@available(iOS 15.0, tvOS 15.0, *)) {
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(_framerate, _framerate, _framerate);
} else {
_displayLink.preferredFramesPerSecond = _framerate;
}
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)displayLinkHandler:(CADisplayLink *)link {
// Rendering does not use DisplayLink, this exists to fool iOS into keeping us running at the desired framerate
}
- (void)waitToRenderTo:(nonnull CAMetalLayer *)layer {
@@ -102,6 +117,10 @@
Log(LOG_I, @"[MetalViewController] viewDidDisappear");
if (_displayLink) {
[_displayLink invalidate];
}
[_renderer shutdown];
_renderer = nil;
}