Revert "feat: bring back CAMetalDisplayLink"

This reverts commit b6c029df
This commit is contained in:
Acaki committed 2025-08-27 15:16:24 +08:00
1 parent 83566ad4e1
commit eb3501e090
7 files changed
+88 -162

No files matched your search

+1 -9
View File
@@ -52,15 +52,7 @@
self.statsOverlay = [[NSUserDefaults standardUserDefaults] boolForKey:@"statsOverlay"]; self.statsOverlay = [[NSUserDefaults standardUserDefaults] boolForKey:@"statsOverlay"];
self.enableGraphs = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableGraphs"]; self.enableGraphs = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableGraphs"];
self.graphOpacity = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"graphOpacity"]]; self.graphOpacity = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"graphOpacity"]];
// Set rendering backend based on iOS version and user preference self.renderingBackend = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"renderingBackend"]];
NSInteger savedBackend = [[NSUserDefaults standardUserDefaults] integerForKey:@"renderingBackend"];
if (@available(iOS 17.0, *)) {
// iOS 17+ can use Performance mode (Metal renderer)
self.renderingBackend = [NSNumber numberWithInteger:savedBackend];
} else {
// iOS < 17 must use Balanced mode (AVSB renderer)
self.renderingBackend = [NSNumber numberWithInteger:RENDER_AVSB];
}
self.framePacingMode = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"framePacingMode"]]; self.framePacingMode = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"framePacingMode"]];
NSInteger _screenSize = [[NSUserDefaults standardUserDefaults] integerForKey:@"streamResolution"]; NSInteger _screenSize = [[NSUserDefaults standardUserDefaults] integerForKey:@"streamResolution"];
+2 -3
View File
@@ -24,9 +24,8 @@
@property (nonatomic) BOOL hdrEnabled; @property (nonatomic) BOOL hdrEnabled;
- (nonnull instancetype)initWithMetalDevice:(nonnull id<MTLDevice>)device drawablePixelFormat:(MTLPixelFormat)drawablePixelFormat framerate:(float)framerate hdrEnabled:(BOOL)hdrEnabled; - (nonnull instancetype)initWithMetalDevice:(nonnull id<MTLDevice>)device drawablePixelFormat:(MTLPixelFormat)drawablePixelFormat framerate:(float)framerate hdrEnabled:(BOOL)hdrEnabled;
- (void)renderFrame:(nonnull Frame *)frame withDrawable:(nonnull id<CAMetalDrawable>)drawable API_AVAILABLE(ios(17.0)); - (void)renderFrame:(nonnull Frame *)frame toLayer:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(13.0));
- (void)renderFrame:(nonnull Frame *)frame withDrawable:(nonnull id<CAMetalDrawable>)drawable targetPresentationTimestamp:(CFTimeInterval)targetPresentationTimestamp API_AVAILABLE(ios(17.0)); - (void)waitToRenderTo:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(13.0));
- (void)waitToRenderTo:(nonnull CAMetalLayer *)layer;
- (void)drawableResize:(CGSize)drawableSize; - (void)drawableResize:(CGSize)drawableSize;
- (void)shutdown; - (void)shutdown;
+32 -40
View File
@@ -557,29 +557,21 @@ CFStringRef __currentColorSpace;
return YES; return YES;
} }
- (void)renderFrame:(Frame *)frame toLayer:(CAMetalLayer *)layer {
- (void)renderFrame:(Frame *)frame withDrawable:(id<CAMetalDrawable>)drawable targetPresentationTimestamp:(CFTimeInterval)targetPresentationTimestamp API_AVAILABLE(ios(17.0)) {
@autoreleasepool { @autoreleasepool {
if (self.isStopping) { if (self.isStopping) {
Log(LOG_I, @"[MetalVideoRenderer] isStopping"); Log(LOG_I, @"[MetalVideoRenderer] isStopping");
return; return;
} }
// Calculate accurate frametime using CAMetalDisplayLink timing
if (self.lastPresented > 0.0f) {
CFTimeInterval frametime = targetPresentationTimestamp - self.lastPresented;
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:(frametime * 1000.0)];
}
self.lastPresented = targetPresentationTimestamp;
// Handle changes to the frame's colorspace from last time we rendered // Handle changes to the frame's colorspace from last time we rendered
BOOL layerDidChange = NO; BOOL layerDidChange = NO;
if (![self updateColorSpaceForFrame:frame toLayer:(CAMetalLayer *)drawable.layer layerDidChange:&layerDidChange]) { if (![self updateColorSpaceForFrame:frame toLayer:layer layerDidChange:&layerDidChange]) {
return; return;
} }
// Handle changes to the video size or drawable size // Handle changes to the video size or drawable size
if (![self updateVideoRegionSizeForFrame:frame toLayer:(CAMetalLayer *)drawable.layer]) { if (![self updateVideoRegionSizeForFrame:frame toLayer:layer]) {
return; return;
} }
@@ -604,6 +596,13 @@ CFStringRef __currentColorSpace;
} }
} }
// Get the next drawable early to get its pixel format
id<CAMetalDrawable> drawable = [layer nextDrawable];
if (!drawable) {
Log(LOG_E, @"Failed to get nextDrawable");
return;
}
// Get the framebuffer pixel format for pipeline creation // Get the framebuffer pixel format for pipeline creation
MTLPixelFormat framebufferPixelFormat = drawable.texture.pixelFormat; MTLPixelFormat framebufferPixelFormat = drawable.texture.pixelFormat;
@@ -710,7 +709,7 @@ CFStringRef __currentColorSpace;
[renderEncoder setVertexBuffer:_VideoVertexBuffer offset:0 atIndex:0]; [renderEncoder setVertexBuffer:_VideoVertexBuffer offset:0 atIndex:0];
[renderEncoder setFragmentBuffer:_CscParamsBuffer offset:0 atIndex:0]; [renderEncoder setFragmentBuffer:_CscParamsBuffer offset:0 atIndex:0];
#if !TARGET_OS_TV #if !TARGET_OS_TV
if (((CAMetalLayer *)drawable.layer).pixelFormat == MTLPixelFormatRGBA16Float) { if (layer.pixelFormat == MTLPixelFormatRGBA16Float) {
[self pollCurrentEDRHeadroom]; [self pollCurrentEDRHeadroom];
[renderEncoder setFragmentBytes:&_currentEDRHeadroom length:sizeof(float) atIndex:1]; [renderEncoder setFragmentBytes:&_currentEDRHeadroom length:sizeof(float) atIndex:1];
} }
@@ -718,30 +717,25 @@ CFStringRef __currentColorSpace;
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4]; [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
[renderEncoder endEncoding]; [renderEncoder endEncoding];
__block MetalVideoRenderer *strongSelf = self;
#if !TARGET_OS_SIMULATOR
[drawable addPresentedHandler:^(id<MTLDrawable> d) {
if (strongSelf.lastPresented > 0.0f) {
CFTimeInterval frametime = d.presentedTime - strongSelf.lastPresented;
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:(frametime * 1000.0)];
}
strongSelf.lastPresented = d.presentedTime;
}];
#endif
// signal semaphore, compute GPU time average, and clear textures // signal semaphore, compute GPU time average, and clear textures
__block dispatch_semaphore_t block_semaphore = _inFlightSemaphore; __block dispatch_semaphore_t block_semaphore = _inFlightSemaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) { [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
dispatch_semaphore_signal(block_semaphore); dispatch_semaphore_signal(block_semaphore);
if (cb.error) { const CFTimeInterval GPUTime = cb.GPUEndTime - cb.GPUStartTime;
Log(LOG_E, @"Command buffer error: %@", cb.error); const double alpha = 0.25f;
// Request IDR frame to recover from rendering error self->_averageGPUTime = (GPUTime * alpha) + (self->_averageGPUTime * (1.0 - alpha));
LiRequestIdrFrame();
// Clear the command queue to reset GPU state
if (cb.status == MTLCommandBufferStatusError) {
NSError *error = cb.error;
if (error.code == 2 || error.code == 4) { // GPU timeout or submissions ignored
Log(LOG_W, @"GPU timeout detected, flushing command queue");
// Force texture cache flush
CVMetalTextureCacheFlush(self->_textureCache, 0);
}
}
} else {
const CFTimeInterval GPUTime = cb.GPUEndTime - cb.GPUStartTime;
const double alpha = 0.25f;
self->_averageGPUTime = (GPUTime * alpha) + (self->_averageGPUTime * (1.0 - alpha));
}
// Free textures after completion of rendering // Free textures after completion of rendering
for (size_t i = 0; i < planes; i++) { for (size_t i = 0; i < planes; i++) {
@@ -754,10 +748,15 @@ CFStringRef __currentColorSpace;
CVMetalTextureCacheFlush(self->_textureCache, 0); CVMetalTextureCacheFlush(self->_textureCache, 0);
}]; }];
// CAMetalDisplayLink handles timing automatically, so we don't use afterMinimumDuration #if TARGET_OS_SIMULATOR
[commandBuffer presentDrawable:drawable]; [commandBuffer presentDrawable:drawable];
#else
// present for a minimum duration for best frame pacing
[commandBuffer presentDrawable:drawable afterMinimumDuration:1.0f / _framerate];
#endif
[commandBuffer commit]; [commandBuffer commit];
[commandBuffer waitUntilCompleted];
} }
} }
@@ -766,14 +765,7 @@ CFStringRef __currentColorSpace;
// by any stage in the Metal pipeline (CPU, GPU, Metal, Drivers, etc.). // by any stage in the Metal pipeline (CPU, GPU, Metal, Drivers, etc.).
if (!self.isStopping) { if (!self.isStopping) {
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)); // 100ms dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)); // 100ms
long result = dispatch_semaphore_wait(_inFlightSemaphore, timeout); dispatch_semaphore_wait(_inFlightSemaphore, timeout);
if (result != 0) {
Log(LOG_W, @"MetalVideoRenderer semaphore timeout - requesting IDR frame");
LiRequestIdrFrame(); // Request recovery
// Force flush the texture cache when we timeout
CVMetalTextureCacheFlush(_textureCache, 0);
}
} }
} }
+2 -3
View File
@@ -15,14 +15,13 @@
@protocol MetalViewDelegate <NSObject> @protocol MetalViewDelegate <NSObject>
- (void)drawableResize:(CGSize)size; - (void)drawableResize:(CGSize)size;
- (void)renderWithDrawable:(nonnull id<CAMetalDrawable>)drawable toLayer:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(17.0)); - (void)renderTo:(nonnull CAMetalLayer *)layer;
- (void)renderWithDrawable:(nonnull id<CAMetalDrawable>)drawable toLayer:(nonnull CAMetalLayer *)layer targetPresentationTimestamp:(CFTimeInterval)targetPresentationTimestamp API_AVAILABLE(ios(17.0));
- (void)waitToRenderTo:(nonnull CAMetalLayer *)layer; - (void)waitToRenderTo:(nonnull CAMetalLayer *)layer;
@end @end
// The Metal game view base class. // The Metal game view base class.
@interface MetalView : UIView <CALayerDelegate, CAMetalDisplayLinkDelegate> @interface MetalView : UIView <CALayerDelegate>
@property (nonatomic, nonnull, readonly) CAMetalLayer *metalLayer; @property (nonatomic, nonnull, readonly) CAMetalLayer *metalLayer;
@property (nonatomic, nullable) id<MetalViewDelegate> delegate; @property (nonatomic, nullable) id<MetalViewDelegate> delegate;
+8 -78
View File
@@ -11,12 +11,10 @@
#import "MetalView.h" #import "MetalView.h"
#import "MetalConfig.h" #import "MetalConfig.h"
#import <QuartzCore/CAMetalDisplayLink.h>
@implementation MetalView { @implementation MetalView {
// The secondary thread containing the render loop. // The secondary thread containing the render loop.
NSThread *_renderThread; NSThread *_renderThread;
CAMetalDisplayLink *_metalDisplayLink API_AVAILABLE(ios(17.0));
} }
#pragma mark - Initialization and Setup. #pragma mark - Initialization and Setup.
@@ -42,43 +40,15 @@
self.layer.delegate = self; self.layer.delegate = self;
} }
- (void)setFramerate:(float)framerate {
_framerate = framerate;
if (@available(iOS 17.0, *)) {
if (_metalDisplayLink) {
_metalDisplayLink.preferredFrameRateRange = CAFrameRateRangeMake(framerate, framerate, framerate);
}
}
}
- (void)shutdown { - (void)shutdown {
// First cancel the thread to stop the run loop
if (_renderThread) { if (_renderThread) {
Log(LOG_I, @"[MetalView] sending renderThread a cancel message"); Log(LOG_I, @"[MetalView] sending renderThread a cancel message");
[_renderThread cancel]; [_renderThread cancel];
// Invalidate metal display link to stop callbacks
if (@available(iOS 17.0, *)) {
if (_metalDisplayLink) {
Log(LOG_I, @"[MetalView] invalidating metal display link");
[_metalDisplayLink invalidate];
_metalDisplayLink = nil;
}
}
// Now wait for thread to finish
Log(LOG_I, @"[MetalView] waiting on renderThread to finish"); Log(LOG_I, @"[MetalView] waiting on renderThread to finish");
NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:2.0]; // 2 second timeout while (!_renderThread.isFinished) {
while (!_renderThread.isFinished && [timeout timeIntervalSinceNow] > 0) { usleep(100);
usleep(1000);
}
if (_renderThread.isFinished) {
Log(LOG_I, @"[MetalView] renderThread has finished");
} else {
Log(LOG_E, @"[MetalView] renderThread failed to finish in time");
} }
Log(LOG_I, @"[MetalView] renderThread has finished");
_renderThread = nil; _renderThread = nil;
} }
} }
@@ -91,26 +61,6 @@
[self movedToWindow]; [self movedToWindow];
} }
- (void)metalDisplayLink:(CAMetalDisplayLink *)link needsUpdate:(CAMetalDisplayLinkUpdate *)update API_AVAILABLE(ios(17.0)) {
// Skip rendering if we're shutting down
if ([NSThread currentThread].isCancelled) {
return;
}
@autoreleasepool {
[self.delegate waitToRenderTo:self.metalLayer];
id<CAMetalDrawable> drawable = update.drawable;
if (drawable) {
// Pass timing information to the renderer
if ([self.delegate respondsToSelector:@selector(renderWithDrawable:toLayer:targetPresentationTimestamp:)]) {
[self.delegate renderWithDrawable:drawable toLayer:self.metalLayer targetPresentationTimestamp:update.targetPresentationTimestamp];
} else {
[self.delegate renderWithDrawable:drawable toLayer:self.metalLayer];
}
}
}
}
- (void)movedToWindow { - (void)movedToWindow {
if (!self.window) { if (!self.window) {
Log(LOG_I, @"[MetalView] movedToWindow(nil): shutting down..."); Log(LOG_I, @"[MetalView] movedToWindow(nil): shutting down...");
@@ -118,32 +68,12 @@
return; return;
} }
if (@available(iOS 17.0, *)) { // Render on a new thread
_metalDisplayLink = [[CAMetalDisplayLink alloc] initWithMetalLayer:_metalLayer];
_metalDisplayLink.delegate = self;
_metalDisplayLink.preferredFrameRateRange = CAFrameRateRangeMake(_framerate, _framerate, _framerate);
Log(LOG_I, @"[MetalView] Using CAMetalDisplayLink for optimal Metal rendering");
}
// Start the display link on a background thread
_renderThread = [[NSThread alloc] initWithBlock:^{ _renderThread = [[NSThread alloc] initWithBlock:^{
// Add metal display link to this thread's run loop while (![NSThread currentThread].isCancelled) {
if (@available(iOS 17.0, *)) {
[self->_metalDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
// Keep the run loop alive, checking for cancellation regularly
while (![NSThread currentThread].isCancelled && self->_metalDisplayLink) {
@autoreleasepool { @autoreleasepool {
// Run the run loop for a short time to allow checking cancellation [self.delegate waitToRenderTo:self.metalLayer];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; [self.delegate renderTo:self.metalLayer];
}
}
// Remove from run loop if still valid
if (@available(iOS 17.0, *)) {
if (self->_metalDisplayLink) {
[self->_metalDisplayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
} }
} }
Log(LOG_I, @"[MetalView] renderThread is exiting"); Log(LOG_I, @"[MetalView] renderThread is exiting");
@@ -151,7 +81,7 @@
_renderThread.name = @"MetalVideoRenderer"; _renderThread.name = @"MetalVideoRenderer";
_renderThread.qualityOfService = NSQualityOfServiceUserInteractive; _renderThread.qualityOfService = NSQualityOfServiceUserInteractive;
[_renderThread start]; [_renderThread start];
Log(LOG_I, @"[MetalView] started renderThread with CAMetalDisplayLink at %f fps", _framerate); Log(LOG_I, @"[MetalView] started renderThread %@", _renderThread);
// Perform any actions that need to know the size and scale of the drawable. When UIKit calls // Perform any actions that need to know the size and scale of the drawable. When UIKit calls
// didMoveToWindow after the view initialization, this is the first opportunity to notify // didMoveToWindow after the view initialization, this is the first opportunity to notify
+32 -14
View File
@@ -18,6 +18,7 @@
MetalView *_metalView; MetalView *_metalView;
MetalVideoRenderer *_renderer; MetalVideoRenderer *_renderer;
MetricsHandler _metricsHandler; MetricsHandler _metricsHandler;
CADisplayLink *_displayLink;
} }
- (nonnull instancetype)initWithFrame:(CGRect)bounds framerate:(float)framerate enableHdr:(BOOL)enableHdr metricsHandler:(MetricsHandler)metricsHandler { - (nonnull instancetype)initWithFrame:(CGRect)bounds framerate:(float)framerate enableHdr:(BOOL)enableHdr metricsHandler:(MetricsHandler)metricsHandler {
@@ -73,6 +74,20 @@
// Initialize the renderer-dependent view properties. // Initialize the renderer-dependent view properties.
view.metalLayer.pixelFormat = renderer.colorPixelFormat; view.metalLayer.pixelFormat = renderer.colorPixelFormat;
view.metalLayer.maximumDrawableCount = 3; 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 { - (void)waitToRenderTo:(nonnull CAMetalLayer *)layer {
@@ -90,23 +105,17 @@
[_frameQueue waitForEnqueue]; [_frameQueue waitForEnqueue];
} }
- (void)renderWithDrawable:(nonnull id<CAMetalDrawable>)drawable toLayer:(nonnull CAMetalLayer *)layer API_AVAILABLE(ios(17.0)) { /// Draw frame (used by manual loop)
- (void)renderTo:(nonnull CAMetalLayer *)layer {
CFTimeInterval timeout = (1.0f / _framerate) - _renderer.averageGPUTime; CFTimeInterval timeout = (1.0f / _framerate) - _renderer.averageGPUTime;
Frame *frame = [_frameQueue dequeueWithTimeout:timeout]; Frame *frame = [_frameQueue dequeueWithTimeout:timeout];
if (!_renderer.isStopping) { if (!_renderer.isStopping) {
// Only render if not paused // Only render if not paused
if (frame) { if (frame) {
[_renderer renderFrame:frame withDrawable:drawable]; if (@available(iOS 13.0, *)) {
} [_renderer renderFrame:frame toLayer:layer];
} }
}
- (void)renderWithDrawable:(nonnull id<CAMetalDrawable>)drawable toLayer:(nonnull CAMetalLayer *)layer targetPresentationTimestamp:(CFTimeInterval)targetPresentationTimestamp API_AVAILABLE(ios(17.0)) {
if (!_renderer.isStopping) {
CFTimeInterval timeout = (1.0f / _framerate) - _renderer.averageGPUTime;
Frame *frame = [_frameQueue dequeueWithTimeout:timeout];
if (frame) {
[_renderer renderFrame:frame withDrawable:drawable targetPresentationTimestamp:targetPresentationTimestamp];
} }
} else { } else {
// When paused, we still dequeue frames to prevent accumulation // When paused, we still dequeue frames to prevent accumulation
@@ -120,6 +129,9 @@
} }
- (void)pauseRendering { - (void)pauseRendering {
if (_displayLink) {
_displayLink.paused = YES;
}
if (_renderer) { if (_renderer) {
_renderer.isStopping = YES; _renderer.isStopping = YES;
} }
@@ -130,6 +142,9 @@
if (_renderer) { if (_renderer) {
_renderer.isStopping = NO; _renderer.isStopping = NO;
} }
if (_displayLink) {
_displayLink.paused = NO;
}
Log(LOG_I, @"[MetalViewController] Rendering resumed"); Log(LOG_I, @"[MetalViewController] Rendering resumed");
} }
@@ -138,13 +153,16 @@
Log(LOG_I, @"[MetalViewController] viewDidDisappear"); Log(LOG_I, @"[MetalViewController] viewDidDisappear");
// Shutdown the renderer first if (_displayLink) {
[_displayLink invalidate];
_displayLink = nil;
}
if (_renderer) { if (_renderer) {
[_renderer shutdown]; [_renderer shutdown];
_renderer = nil; _renderer = nil;
} }
// Then shutdown the view's display link
if (_metalView) { if (_metalView) {
_metalView.delegate = nil; _metalView.delegate = nil;
[_metalView shutdown]; [_metalView shutdown];
@@ -320,7 +320,7 @@ BOOL isCustomResolution(int resolutionSelected) {
selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available
name:UIDeviceOrientationDidChangeNotification name:UIDeviceOrientationDidChangeNotification
object:nil]; object:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if(self.mainFrameViewController.settingsExpandedInStreamView){ if(self.mainFrameViewController.settingsExpandedInStreamView){
NSInteger responseCode = [self.mainFrameViewController requestForBitrate:self->_bitrate]; NSInteger responseCode = [self.mainFrameViewController requestForBitrate:self->_bitrate];
@@ -570,11 +570,7 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; [self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
[self addSetting:self.framePacingStack ofId:@"framePacingStack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; [self addSetting:self.framePacingStack ofId:@"framePacingStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
[self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" withInfoTag:NO withDynamicLabel:YES to:videoSection]; [self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" withInfoTag:NO withDynamicLabel:YES to:videoSection];
[self addSetting:self.renderingBackendStack ofId:@"renderingBackendStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
// Only show Metal renderer option on iOS 17+ where CAMetalDisplayLink is available
if (@available(iOS 17.0, *)) {
[self addSetting:self.renderingBackendStack ofId:@"renderingBackendStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
}
[videoSection addToParentStack:_parentStack]; [videoSection addToParentStack:_parentStack];
[videoSection setExpanded:YES]; [videoSection setExpanded:YES];
@@ -653,10 +649,10 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.optimizeGamesStack ofId:@"optimizeGamesStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; [self addSetting:self.optimizeGamesStack ofId:@"optimizeGamesStack" withInfoTag:YES withDynamicLabel:NO to:otherSection];
[self addSetting:self.multiControllerStack ofId:@"multiControllerStack" withInfoTag:NO withDynamicLabel:NO to:otherSection]; [self addSetting:self.multiControllerStack ofId:@"multiControllerStack" withInfoTag:NO withDynamicLabel:NO to:otherSection];
[self addSetting:self.softKeyboardToolbarStack ofId:@"softKeyboardToolbarStack" withInfoTag:NO withDynamicLabel:NO to:otherSection]; [self addSetting:self.softKeyboardToolbarStack ofId:@"softKeyboardToolbarStack" withInfoTag:NO withDynamicLabel:NO to:otherSection];
[self addSetting:self.performanceGraphStack ofId:@"performanceGraphStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; [self addSetting:self.performanceGraphStack ofId:@"performanceGraphStack" withInfoTag:YES withDynamicLabel:NO to:otherSection];
[self addDynamicLabelForStack:self.graphOpacityStack]; [self addDynamicLabelForStack:self.graphOpacityStack];
[otherSection addToParentStack:_parentStack]; [otherSection addToParentStack:_parentStack];
[otherSection setExpanded:YES]; [otherSection setExpanded:YES];
@@ -1095,7 +1091,7 @@ BOOL isCustomResolution(int resolutionSelected) {
} }
UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert]; UIAlertController *tipsAlertController = [UIAlertController alertControllerWithTitle: [LocalizationHelper localizedStringForKey:@"Tips"] message:tipText preferredStyle:UIAlertControllerStyleAlert];
@@ -1751,7 +1747,7 @@ BOOL isCustomResolution(int resolutionSelected) {
- (void)framePacingModeChanged:(UISegmentedControl *)sender { - (void)framePacingModeChanged:(UISegmentedControl *)sender {
[self setHidden:sender.selectedSegmentIndex == FramePacingModeLegacy forStack:self.frameQueueSizeStack]; [self setHidden:sender.selectedSegmentIndex == FramePacingModeLegacy forStack:self.frameQueueSizeStack];
[videoSection updateViewForFoldState]; [videoSection updateViewForFoldState];
if(sender.selectedSegmentIndex == FramePacingModeLegacy){ if(sender.selectedSegmentIndex == FramePacingModeLegacy){
[self.enableGraphsSwitch setOn:NO]; [self.enableGraphsSwitch setOn:NO];
[self findDynamicLabelFromStack:_graphOpacityStack].hidden = YES; [self findDynamicLabelFromStack:_graphOpacityStack].hidden = YES;
@@ -1760,20 +1756,20 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.graphOpacityStepper setEnabled:self.enableGraphsSwitch.isOn]; [self.graphOpacityStepper setEnabled:self.enableGraphsSwitch.isOn];
[self setHidden:sender.selectedSegmentIndex == FramePacingModeLegacy forStack:self.performanceGraphStack]; [self setHidden:sender.selectedSegmentIndex == FramePacingModeLegacy forStack:self.performanceGraphStack];
[otherSection updateViewForFoldState]; [otherSection updateViewForFoldState];
/* /*
if (sender.selectedSegmentIndex == FramePacingModeLegacy) { if (sender.selectedSegmentIndex == FramePacingModeLegacy) {
// Legacy mode selected - disable frames to buffer and graph settings // Legacy mode selected - disable frames to buffer and graph settings
[self.frameQueueSizeSlider setEnabled:NO]; [self.frameQueueSizeSlider setEnabled:NO];
[self.enableGraphsSwitch setOn:NO animated:YES]; [self.enableGraphsSwitch setOn:NO animated:YES];
[self.enableGraphsSwitch setEnabled:NO]; [self.enableGraphsSwitch setEnabled:NO];
[self.graphOpacityStepper setEnabled:NO]; [self.graphOpacityStepper setEnabled:NO];
} else { } else {
// Queue mode selected - enable frames to buffer and graph settings // Queue mode selected - enable frames to buffer and graph settings
[self.frameQueueSizeSlider setEnabled:YES]; [self.frameQueueSizeSlider setEnabled:YES];
[self.enableGraphsSwitch setEnabled:YES]; [self.enableGraphsSwitch setEnabled:YES];
if (self.enableGraphsSwitch.isOn) { if (self.enableGraphsSwitch.isOn) {
[self.graphOpacityStepper setEnabled:YES]; [self.graphOpacityStepper setEnabled:YES];
} }
@@ -2304,11 +2300,11 @@ BOOL isCustomResolution(int resolutionSelected) {
} }
- (void) saveSettings { - (void) saveSettings {
if(self.mainFrameViewController.settingsExpandedInStreamView){ if(self.mainFrameViewController.settingsExpandedInStreamView){
[self.mainFrameViewController requestForBitrate:(uint32_t)_bitrate]; [self.mainFrameViewController requestForBitrate:(uint32_t)_bitrate];
} }
DataManager* dataMan = [[DataManager alloc] init]; DataManager* dataMan = [[DataManager alloc] init];
Settings* currentSettings = [dataMan retrieveSettings]; Settings* currentSettings = [dataMan retrieveSettings];