From 11a5d4db1438148984293fde5ea91bcad0d8aaea Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:49:01 +0100 Subject: [PATCH 1/7] Add Xcode 26 CI jobs for iOS and macOS Replace the pipeline-level XCODE_VERSION variable with an explicit xcodeVersion parameter on each Apple job template (ios, macos, test_install_ios, test_install_macos). Every Apple job now passes the Xcode version directly, making it easy to test different versions side by side. New jobs: MacOS_Xcode26 and iOS_Xcode26 (Xcode 26.3, macOS-latest). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/jobs/ios.yml | 5 +++-- .github/jobs/macos.yml | 5 +++-- .github/jobs/test_install_ios.yml | 5 +++-- .github/jobs/test_install_macos.yml | 5 +++-- azure-pipelines.yml | 23 +++++++++++++++++++++-- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/.github/jobs/ios.yml b/.github/jobs/ios.yml index 6af275c49..868d66797 100644 --- a/.github/jobs/ios.yml +++ b/.github/jobs/ios.yml @@ -2,6 +2,7 @@ parameters: name: "" vmImage: "" deploymentTarget: "15" + xcodeVersion: "" jobs: - job: ${{parameters.name}} @@ -15,8 +16,8 @@ jobs: vmImage: ${{parameters.vmImage}} - script: | - sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - displayName: "Select Xcode $(XCODE_VERSION)" + sudo xcode-select --switch /Applications/Xcode_${{parameters.xcodeVersion}}.app/Contents/Developer + displayName: "Select Xcode ${{parameters.xcodeVersion}}" - script: | cmake -G Xcode -B build/iOS -D IOS=ON -D DEPLOYMENT_TARGET=${{parameters.deploymentTarget}} -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D BABYLON_DEBUG_TRACE=ON -D CMAKE_IOS_INSTALL_COMBINED=NO diff --git a/.github/jobs/macos.yml b/.github/jobs/macos.yml index 7a31f775b..7c5352da8 100644 --- a/.github/jobs/macos.yml +++ b/.github/jobs/macos.yml @@ -3,6 +3,7 @@ parameters: vmImage: "" enableSanitizers: false generator: Xcode + xcodeVersion: "" jobs: - job: ${{parameters.name}} @@ -19,8 +20,8 @@ jobs: vmImage: ${{parameters.vmImage}} - script: | - sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - displayName: "Select XCode $(XCODE_VERSION)" + sudo xcode-select --switch /Applications/Xcode_${{parameters.xcodeVersion}}.app/Contents/Developer + displayName: "Select Xcode ${{parameters.xcodeVersion}}" - script: | cmake -G "${{parameters.generator}}" -B build/macOS -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D BABYLON_DEBUG_TRACE=ON -D ENABLE_SANITIZERS=$(SANITIZER_FLAG) -D BABYLON_NATIVE_TESTS_USE_NOOP_METAL_DEVICE=ON diff --git a/.github/jobs/test_install_ios.yml b/.github/jobs/test_install_ios.yml index 194f00a12..1df999bc4 100644 --- a/.github/jobs/test_install_ios.yml +++ b/.github/jobs/test_install_ios.yml @@ -2,6 +2,7 @@ parameters: name: "" vmImage: "" deploymentTarget: "15" + xcodeVersion: "" jobs: - job: ${{parameters.name}} @@ -15,8 +16,8 @@ jobs: vmImage: ${{parameters.vmImage}} - script: | - sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - displayName: "Select Xcode $(XCODE_VERSION)" + sudo xcode-select --switch /Applications/Xcode_${{parameters.xcodeVersion}}.app/Contents/Developer + displayName: "Select Xcode ${{parameters.xcodeVersion}}" - script: | cmake -B build/iOS -G Xcode -D IOS=ON -D DEPLOYMENT_TARGET=${{parameters.deploymentTarget}} -D CMAKE_IOS_INSTALL_COMBINED=NO -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D BABYLON_NATIVE_BUILD_APPS=OFF -D BABYLON_DEBUG_TRACE=ON diff --git a/.github/jobs/test_install_macos.yml b/.github/jobs/test_install_macos.yml index 87f8d6620..2c493c6d8 100644 --- a/.github/jobs/test_install_macos.yml +++ b/.github/jobs/test_install_macos.yml @@ -1,6 +1,7 @@ parameters: name: "" vmImage: "" + xcodeVersion: "" jobs: - job: ${{parameters.name}} @@ -14,8 +15,8 @@ jobs: vmImage: ${{parameters.vmImage}} - script: | - sudo xcode-select --switch /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer - displayName: "Select XCode $(XCODE_VERSION)" + sudo xcode-select --switch /Applications/Xcode_${{parameters.xcodeVersion}}.app/Contents/Developer + displayName: "Select Xcode ${{parameters.xcodeVersion}}" - script: | cmake -B build/macOS -G Xcode -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D BABYLON_NATIVE_BUILD_APPS=OFF -D BABYLON_DEBUG_TRACE=ON diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c590c3651..abdd47908 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,8 +11,6 @@ variables: value: 28.2.13676358 - name: UNITY_BUILD value: true - - name: XCODE_VERSION - value: 16.4 jobs: # Apple @@ -20,31 +18,50 @@ jobs: parameters: name: MacOS vmImage: macOS-latest + xcodeVersion: "16.4" - template: .github/jobs/macos.yml parameters: name: MacOS_Ninja vmImage: macOS-latest + xcodeVersion: "16.4" generator: Ninja Multi-Config - template: .github/jobs/macos.yml parameters: name: MacOS_Sanitizers vmImage: macOS-latest + xcodeVersion: "16.4" enableSanitizers: true - template: .github/jobs/ios.yml parameters: name: iOS_iOS180 vmImage: macOS-latest + xcodeVersion: "16.4" deploymentTarget: 18.0 - template: .github/jobs/ios.yml parameters: name: iOS_iOS175 vmImage: macOS-latest + xcodeVersion: "16.4" deploymentTarget: 17.5 + # Xcode 26 + - template: .github/jobs/macos.yml + parameters: + name: MacOS_Xcode26 + vmImage: macOS-latest + xcodeVersion: "26.3" + + - template: .github/jobs/ios.yml + parameters: + name: iOS_Xcode26 + vmImage: macOS-latest + xcodeVersion: "26.3" + deploymentTarget: 26.0 + # Win32 - template: .github/jobs/win32.yml parameters: @@ -168,6 +185,7 @@ jobs: parameters: name: iOS_Installation vmImage: macOS-latest + xcodeVersion: "16.4" deploymentTarget: 17.2 - template: .github/jobs/test_install_linux.yml parameters: @@ -177,6 +195,7 @@ jobs: parameters: name: MacOS_Installation vmImage: macOS-latest + xcodeVersion: "16.4" - template: .github/jobs/test_install_win32.yml parameters: name: Win32_Installation From 08f1008ed3023198d2ba6494c7f6961401ee372b Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:47:52 +0100 Subject: [PATCH 2/7] Fix deprecated interfaceOrientation on iOS 26 Use effectiveGeometry.interfaceOrientation on iOS 26+ while keeping the old interfaceOrientation call for earlier iOS versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Dependencies/xr/Source/ARKit/XR.mm | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Dependencies/xr/Source/ARKit/XR.mm b/Dependencies/xr/Source/ARKit/XR.mm index d1bf5f6fe..ed0ffb317 100644 --- a/Dependencies/xr/Source/ARKit/XR.mm +++ b/Dependencies/xr/Source/ARKit/XR.mm @@ -259,11 +259,24 @@ - (void) UnlockAnchors { - (UIInterfaceOrientation)orientation { UIApplication* sharedApplication = [UIApplication sharedApplication]; #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) - UIScene* scene = [[[sharedApplication connectedScenes] allObjects] firstObject]; - return [(UIWindowScene*)scene interfaceOrientation]; + UIWindowScene* windowScene = (UIWindowScene*)[[[sharedApplication connectedScenes] allObjects] firstObject]; + if (@available(iOS 26.0, *)) { + return windowScene.effectiveGeometry.interfaceOrientation; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [windowScene interfaceOrientation]; +#pragma clang diagnostic pop #else if (@available(iOS 13.0, *)) { - return [[[[sharedApplication windows] firstObject] windowScene] interfaceOrientation]; + UIWindowScene* windowScene = [[[sharedApplication windows] firstObject] windowScene]; + if (@available(iOS 26.0, *)) { + return windowScene.effectiveGeometry.interfaceOrientation; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [windowScene interfaceOrientation]; +#pragma clang diagnostic pop } else { return [sharedApplication statusBarOrientation]; From 815c4cff0577f5c130cec01d3345fd130d8deb7f Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 20 Mar 2026 18:18:13 +0100 Subject: [PATCH 3/7] Fix deprecated interfaceOrientation in CameraDevice on iOS 26 Use effectiveGeometry.interfaceOrientation on iOS 26+ with pragma guards around the old API for older iOS versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../NativeCamera/Source/Apple/CameraDevice.mm | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Plugins/NativeCamera/Source/Apple/CameraDevice.mm b/Plugins/NativeCamera/Source/Apple/CameraDevice.mm index 7d5b706cb..f044ef868 100644 --- a/Plugins/NativeCamera/Source/Apple/CameraDevice.mm +++ b/Plugins/NativeCamera/Source/Apple/CameraDevice.mm @@ -907,11 +907,24 @@ - (void)updateOrientation { UIApplication* sharedApplication{[UIApplication sharedApplication]}; UIInterfaceOrientation orientation{UIInterfaceOrientationUnknown}; #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) - UIScene* scene{[[[sharedApplication connectedScenes] allObjects] firstObject]}; - orientation = [(UIWindowScene*)scene interfaceOrientation]; + UIWindowScene* windowScene = (UIWindowScene*)[[[sharedApplication connectedScenes] allObjects] firstObject]; + if (@available(iOS 26.0, *)) { + orientation = windowScene.effectiveGeometry.interfaceOrientation; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + orientation = [windowScene interfaceOrientation]; +#pragma clang diagnostic pop + } #else - if (@available(iOS 13.0, *)) { + if (@available(iOS 26.0, *)) { + UIWindowScene* windowScene = [[[sharedApplication windows] firstObject] windowScene]; + orientation = windowScene.effectiveGeometry.interfaceOrientation; + } else if (@available(iOS 13.0, *)) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" orientation = [[[[sharedApplication windows] firstObject] windowScene] interfaceOrientation]; +#pragma clang diagnostic pop } else { orientation = [sharedApplication statusBarOrientation]; From 4fe2bb030fdfb61c555b25c87bf6275458fd2432 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:10:37 +0100 Subject: [PATCH 4/7] Fix UIScreen.mainScreen deprecation in iOS 26 Replace deprecated UIScreen.mainScreen.scale with UIWindowScene.traitCollection.displayScale (iOS 17+). Retains a pragma-suppressed fallback for older iOS versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Core/Graphics/Source/DeviceImpl_iOS.mm | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Core/Graphics/Source/DeviceImpl_iOS.mm b/Core/Graphics/Source/DeviceImpl_iOS.mm index d77d17243..6c601d245 100644 --- a/Core/Graphics/Source/DeviceImpl_iOS.mm +++ b/Core/Graphics/Source/DeviceImpl_iOS.mm @@ -23,7 +23,28 @@ float scale = static_cast(((CAMetalLayer*)window).contentsScale); if (std::isinf(scale) || scale <= 0) { - scale = UIScreen.mainScreen.scale; + // UIScreen.mainScreen is deprecated in iOS 26. + // Prefer getting the scale from the active window scene's trait collection. + if (@available(iOS 17.0, *)) + { + for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) + { + if ([scene isKindOfClass:[UIWindowScene class]]) + { + scale = static_cast(((UIWindowScene*)scene).traitCollection.displayScale); + if (scale > 0) break; + } + } + } + + // Fallback for older iOS versions or if no active scene was found. + if (std::isinf(scale) || scale <= 0) + { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + scale = UIScreen.mainScreen.scale; +#pragma clang diagnostic pop + } } return scale; } From 04d64b5e4f956dd5585cf80656b3073dc60c1424 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:25:25 +0100 Subject: [PATCH 5/7] refactor ios checks --- Core/Graphics/Source/DeviceImpl_iOS.mm | 40 ++++++++++-------- Dependencies/xr/Source/ARKit/XR.mm | 22 +++------- .../NativeCamera/Source/Apple/CameraDevice.mm | 41 ++++++++----------- 3 files changed, 45 insertions(+), 58 deletions(-) diff --git a/Core/Graphics/Source/DeviceImpl_iOS.mm b/Core/Graphics/Source/DeviceImpl_iOS.mm index 6c601d245..011ce2692 100644 --- a/Core/Graphics/Source/DeviceImpl_iOS.mm +++ b/Core/Graphics/Source/DeviceImpl_iOS.mm @@ -5,6 +5,15 @@ #import #import + +namespace +{ + bool IsValidScale(float scale) + { + return !std::isinf(scale) && scale > 0; + } +} + namespace Babylon::Graphics { void DeviceImpl::ConfigureBgfxPlatformData(bgfx::PlatformData& pd, WindowT window) @@ -19,33 +28,32 @@ float DeviceImpl::GetDevicePixelRatio(WindowT window) { // contentsScale can return 0 if it hasn't been set yet. - // Fallback to the scale from the main screen. float scale = static_cast(((CAMetalLayer*)window).contentsScale); - if (std::isinf(scale) || scale <= 0) + if (IsValidScale(scale)) { - // UIScreen.mainScreen is deprecated in iOS 26. - // Prefer getting the scale from the active window scene's trait collection. - if (@available(iOS 17.0, *)) + return scale; + } + + // Prefer getting the scale from the active window scene's trait collection. + if (@available(iOS 17.0, *)) + { + for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) { - for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) + if ([scene isKindOfClass:[UIWindowScene class]]) { - if ([scene isKindOfClass:[UIWindowScene class]]) + scale = static_cast(((UIWindowScene*)scene).traitCollection.displayScale); + if (IsValidScale(scale)) { - scale = static_cast(((UIWindowScene*)scene).traitCollection.displayScale); - if (scale > 0) break; + return scale; } } } + } - // Fallback for older iOS versions or if no active scene was found. - if (std::isinf(scale) || scale <= 0) - { + // Fallback for older iOS versions or if no active scene was found. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - scale = UIScreen.mainScreen.scale; + return UIScreen.mainScreen.scale; #pragma clang diagnostic pop - } - } - return scale; } } diff --git a/Dependencies/xr/Source/ARKit/XR.mm b/Dependencies/xr/Source/ARKit/XR.mm index ed0ffb317..82a8ed6c9 100644 --- a/Dependencies/xr/Source/ARKit/XR.mm +++ b/Dependencies/xr/Source/ARKit/XR.mm @@ -257,31 +257,19 @@ - (void) UnlockAnchors { Returns the orientation of the app */ - (UIInterfaceOrientation)orientation { - UIApplication* sharedApplication = [UIApplication sharedApplication]; -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) - UIWindowScene* windowScene = (UIWindowScene*)[[[sharedApplication connectedScenes] allObjects] firstObject]; + UIApplication* app = [UIApplication sharedApplication]; if (@available(iOS 26.0, *)) { + UIWindowScene* windowScene = (UIWindowScene*)[[[app connectedScenes] allObjects] firstObject]; return windowScene.effectiveGeometry.interfaceOrientation; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [windowScene interfaceOrientation]; -#pragma clang diagnostic pop -#else if (@available(iOS 13.0, *)) { - UIWindowScene* windowScene = [[[sharedApplication windows] firstObject] windowScene]; - if (@available(iOS 26.0, *)) { - return windowScene.effectiveGeometry.interfaceOrientation; - } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" + UIWindowScene* windowScene = [[[app windows] firstObject] windowScene]; return [windowScene interfaceOrientation]; -#pragma clang diagnostic pop - } - else { - return [sharedApplication statusBarOrientation]; } -#endif + return [app statusBarOrientation]; +#pragma clang diagnostic pop } /** diff --git a/Plugins/NativeCamera/Source/Apple/CameraDevice.mm b/Plugins/NativeCamera/Source/Apple/CameraDevice.mm index f044ef868..a3aaa9b36 100644 --- a/Plugins/NativeCamera/Source/Apple/CameraDevice.mm +++ b/Plugins/NativeCamera/Source/Apple/CameraDevice.mm @@ -900,36 +900,27 @@ - (void) reset { } #if (TARGET_OS_IPHONE) -/** - Updates target video orientation. -*/ -- (void)updateOrientation { - UIApplication* sharedApplication{[UIApplication sharedApplication]}; - UIInterfaceOrientation orientation{UIInterfaceOrientationUnknown}; -#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) - UIWindowScene* windowScene = (UIWindowScene*)[[[sharedApplication connectedScenes] allObjects] firstObject]; + +static UIInterfaceOrientation GetCurrentInterfaceOrientation(UIApplication* app) { if (@available(iOS 26.0, *)) { - orientation = windowScene.effectiveGeometry.interfaceOrientation; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - orientation = [windowScene interfaceOrientation]; -#pragma clang diagnostic pop + UIWindowScene* windowScene = (UIWindowScene*)[[[app connectedScenes] allObjects] firstObject]; + return windowScene.effectiveGeometry.interfaceOrientation; } -#else - if (@available(iOS 26.0, *)) { - UIWindowScene* windowScene = [[[sharedApplication windows] firstObject] windowScene]; - orientation = windowScene.effectiveGeometry.interfaceOrientation; - } else if (@available(iOS 13.0, *)) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - orientation = [[[[sharedApplication windows] firstObject] windowScene] interfaceOrientation]; -#pragma clang diagnostic pop - } - else { - orientation = [sharedApplication statusBarOrientation]; + if (@available(iOS 13.0, *)) { + UIWindowScene* windowScene = [[[app windows] firstObject] windowScene]; + return [windowScene interfaceOrientation]; } -#endif + return [app statusBarOrientation]; +#pragma clang diagnostic pop +} + +/** + Updates target video orientation. +*/ +- (void)updateOrientation { + UIInterfaceOrientation orientation = GetCurrentInterfaceOrientation([UIApplication sharedApplication]); // Convert from UIInterfaceOrientation to VideoOrientation. switch (orientation) From 80f12289f4e07a49d24f56ad174bf303fc8b8b96 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:20:55 +0200 Subject: [PATCH 6/7] use 26.4 --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 687a1bc91..f8f1c06d3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,13 +51,13 @@ jobs: parameters: name: MacOS_Xcode26 vmImage: macOS-latest - xcodeVersion: "26.3" + xcodeVersion: "26.4" - template: .github/jobs/ios.yml parameters: name: iOS_Xcode26 vmImage: macOS-latest - xcodeVersion: "26.3" + xcodeVersion: "26.4" deploymentTarget: 26.0 # Win32 From 79a50cffb4f30c576e8dbf790c13522ba7ebe546 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:10:53 +0200 Subject: [PATCH 7/7] back to Xcode 26.3 --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f8f1c06d3..687a1bc91 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -51,13 +51,13 @@ jobs: parameters: name: MacOS_Xcode26 vmImage: macOS-latest - xcodeVersion: "26.4" + xcodeVersion: "26.3" - template: .github/jobs/ios.yml parameters: name: iOS_Xcode26 vmImage: macOS-latest - xcodeVersion: "26.4" + xcodeVersion: "26.3" deploymentTarget: 26.0 # Win32