Skip to content

Native visionOS platform support #105628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,11 @@ if env["scu_build"]:
# are actually handled to change compile options, etc.
detect.configure(env)

print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".')
platform_string = env["platform"]
if env.get("simulator"):
platform_string += " (simulator)"
print(f'Building for platform "{platform_string}", architecture "{env["arch"]}", target "{env["target"]}".')

if env.dev_build:
print_info("Developer build, with debug optimization level and debug symbols (unless overridden).")

Expand Down
2 changes: 1 addition & 1 deletion core/input/input_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def make_default_controller_mappings(target, source, env):
"Windows": "WINDOWS",
"Mac OS X": "MACOS",
"Android": "ANDROID",
"iOS": "IOS",
"iOS": "APPLE_EMBEDDED",
"Web": "WEB",
}

Expand Down
2 changes: 1 addition & 1 deletion core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ bool OS::has_feature(const String &p_feature) {
}
#endif

#if defined(IOS_SIMULATOR)
#if defined(IOS_SIMULATOR) || defined(VISIONOS_SIMULATOR)
if (p_feature == "simulator") {
return true;
}
Expand Down
13 changes: 13 additions & 0 deletions doc/classes/EditorExportPlatformAppleEmbedded.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorExportPlatformAppleEmbedded" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Base class for the Apple embedded platform exporters (iOS and visionOS).
</brief_description>
<description>
The base class for Apple embedded platform exporters. These include iOS and visionOS, but not macOS. See the classes inheriting from this one for more details.
</description>
<tutorials>
<link title="Exporting for iOS">$DOCS_URL/tutorials/export/exporting_for_ios.html</link>
<link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link>
</tutorials>
</class>
2 changes: 1 addition & 1 deletion doc/classes/EditorExportPlatformPC.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Base class for the desktop platform exporter (Windows and Linux/BSD).
</brief_description>
<description>
The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting this one for more details.
The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting from this one for more details.
</description>
<tutorials>
<link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link>
Expand Down
75 changes: 63 additions & 12 deletions doc/classes/EditorExportPlugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,57 @@
If no modifications are needed, then an empty [PackedByteArray] should be returned.
</description>
</method>
<method name="add_apple_embedded_platform_bundle_file">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds an Apple embedded platform bundle file from the given [param path] to the exported project.
</description>
</method>
<method name="add_apple_embedded_platform_cpp_code">
<return type="void" />
<param index="0" name="code" type="String" />
<description>
Adds C++ code to the Apple embedded platform export. The final code is created from the code appended by each active export plugin.
</description>
</method>
<method name="add_apple_embedded_platform_embedded_framework">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a dynamic library (*.dylib, *.framework) to the Linking Phase in the Apple embedded platform's Xcode project and embeds it into the resulting binary.
[b]Note:[/b] For static libraries (*.a), this works in the same way as [method add_apple_embedded_platform_framework].
[b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
</description>
</method>
<method name="add_apple_embedded_platform_framework">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the Apple embedded platform's Xcode project.
</description>
</method>
<method name="add_apple_embedded_platform_linker_flags">
<return type="void" />
<param index="0" name="flags" type="String" />
<description>
Adds linker flags for the Apple embedded platform export.
</description>
</method>
<method name="add_apple_embedded_platform_plist_content">
<return type="void" />
<param index="0" name="plist_content" type="String" />
<description>
Adds additional fields to the Apple embedded platform's project Info.plist file.
</description>
</method>
<method name="add_apple_embedded_platform_project_static_lib">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library from the given [param path] to the Apple embedded platform project.
</description>
</method>
<method name="add_file">
<return type="void" />
<param index="0" name="path" type="String" />
Expand All @@ -262,55 +313,55 @@
[param file] will not be imported, so consider using [method _customize_resource] to remap imported resources.
</description>
</method>
<method name="add_ios_bundle_file">
<method name="add_ios_bundle_file" deprecated="Use [method add_apple_embedded_platform_bundle_file] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds an iOS bundle file from the given [param path] to the exported project.
</description>
</method>
<method name="add_ios_cpp_code">
<method name="add_ios_cpp_code" deprecated="Use [method add_apple_embedded_platform_cpp_code] instead.">
<return type="void" />
<param index="0" name="code" type="String" />
<description>
Adds a C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
Adds C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
</description>
</method>
<method name="add_ios_embedded_framework">
<method name="add_ios_embedded_framework" deprecated="Use [method add_apple_embedded_platform_embedded_framework] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project and embeds it into resulting binary.
[b]Note:[/b] For static libraries (*.a) works in same way as [method add_ios_framework].
[b]Note:[/b] For static libraries (*.a), this works the in same way as [method add_apple_embedded_platform_framework].
[b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
</description>
</method>
<method name="add_ios_framework">
<method name="add_ios_framework" deprecated="Use [method add_apple_embedded_platform_framework] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library (*.a) or dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project.
Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the iOS Xcode project.
</description>
</method>
<method name="add_ios_linker_flags">
<method name="add_ios_linker_flags" deprecated="Use [method add_apple_embedded_platform_linker_flags] instead.">
<return type="void" />
<param index="0" name="flags" type="String" />
<description>
Adds linker flags for the iOS export.
</description>
</method>
<method name="add_ios_plist_content">
<method name="add_ios_plist_content" deprecated="Use [method add_apple_embedded_platform_plist_content] instead.">
<return type="void" />
<param index="0" name="plist_content" type="String" />
<description>
Adds content for iOS Property List files.
Adds additional fields to the iOS project Info.plist file.
</description>
</method>
<method name="add_ios_project_static_lib">
<method name="add_ios_project_static_lib" deprecated="Use [method add_apple_embedded_platform_project_static_lib] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static lib from the given [param path] to the iOS project.
Adds a static library from the given [param path] to the iOS project.
</description>
</method>
<method name="add_macos_plugin_file">
Expand Down
8 changes: 8 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,9 @@
<member name="display/display_server/driver.macos" type="String" setter="" getter="">
MacOS override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.visionos" type="String" setter="" getter="">
visionOS override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.windows" type="String" setter="" getter="">
Windows override for [member display/display_server/driver].
</member>
Expand Down Expand Up @@ -3163,6 +3166,11 @@
- [code]metal[/code] (default), Metal from native drivers, only supported on Apple Silicon Macs. On Intel Macs, it will automatically fall back to [code]vulkan[/code] as Metal support is not implemented.
- [code]vulkan[/code], Vulkan over Metal via MoltenVK, supported on both Apple Silicon and Intel Macs.
</member>
<member name="rendering/rendering_device/driver.visionos" type="String" setter="" getter="" default="&quot;metal&quot;">
visionOS override for [member rendering/rendering_device/driver].
Only one option is supported:
- [code]metal[/code] (default), Metal from native drivers.
</member>
<member name="rendering/rendering_device/driver.windows" type="String" setter="" getter="" default="&quot;vulkan&quot;">
Windows override for [member rendering/rendering_device/driver].
Two options are supported:
Expand Down
9 changes: 5 additions & 4 deletions drivers/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ SConscript("windows/SCsub")

# Sounds drivers
SConscript("alsa/SCsub")
if env["platform"] == "ios" or env["platform"] == "macos":
SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
Expand All @@ -28,16 +26,19 @@ if env["xaudio2"]:
SConscript("xaudio2/SCsub")

# Shared Apple platform drivers
if env["platform"] in ["macos", "ios"]:
if env["platform"] in ["macos", "ios", "visionos"]:
SConscript("apple/SCsub")
SConscript("coreaudio/SCsub")
if env["platform"] in ["ios", "visionos"]:
SConscript("apple_embedded/SCsub")

# Accessibility
if env["accesskit"] and env["platform"] in ["macos", "windows", "linuxbsd"]:
SConscript("accesskit/SCsub")

# Midi drivers
SConscript("alsamidi/SCsub")
if env["platform"] in ["macos", "ios"]:
if env["platform"] in ["macos"]:
SConscript("coremidi/SCsub")
SConscript("winmidi/SCsub")

Expand Down
16 changes: 16 additions & 0 deletions drivers/apple_embedded/SCsub
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *

Import("env")

env_apple_embedded = env.Clone()

# Enable module support
env_apple_embedded.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])

# Use bundled Vulkan headers
vulkan_dir = "#thirdparty/vulkan"
env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])

# Driver source files
env_apple_embedded.add_source_files(env.drivers_sources, "*.mm")
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**************************************************************************/
/* app_delegate.h */
/* app_delegate_service.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
Expand Down Expand Up @@ -32,18 +32,11 @@

#import <UIKit/UIKit.h>

@class ViewController;
@class GDTViewController;

// FIXME: Add support for both OpenGL and Vulkan when OpenGL is implemented again,
// so it can't be done with compilation time branching.
//#if defined(GLES3_ENABLED)
//@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//#endif
//#if defined(VULKAN_ENABLED)
@interface AppDelegate : NSObject <UIApplicationDelegate>
//#endif
@interface GDTAppDelegateService : NSObject <UIApplicationDelegate>

@property(strong, nonatomic) UIWindow *window;
@property(strong, class, readonly, nonatomic) ViewController *viewController;
@property(strong, class, readonly, nonatomic) GDTViewController *viewController;

@end
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**************************************************************************/
/* app_delegate.mm */
/* app_delegate_service.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
Expand Down Expand Up @@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#import "app_delegate.h"
#import "app_delegate_service.h"

#import "godot_view.h"
#import "os_ios.h"
#import "godot_view_apple_embedded.h"
#import "os_apple_embedded.h"
#import "view_controller.h"

#include "core/config/project_settings.h"
Expand All @@ -46,10 +46,10 @@
extern int gargc;
extern char **gargv;

extern int ios_main(int, char **);
extern void ios_finish();
extern int apple_embedded_main(int, char **);
extern void apple_embedded_finish();

@implementation AppDelegate
@implementation GDTAppDelegateService

enum {
SESSION_CATEGORY_AMBIENT,
Expand All @@ -60,9 +60,9 @@ @implementation AppDelegate
SESSION_CATEGORY_SOLO_AMBIENT
};

static ViewController *mainViewController = nil;
static GDTViewController *mainViewController = nil;

+ (ViewController *)viewController {
+ (GDTViewController *)viewController {
return mainViewController;
}

Expand All @@ -71,20 +71,23 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// TODO: logo screen is not displayed while shaders are compiling
// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController

CGRect windowBounds = [[UIScreen mainScreen] bounds];

#if !defined(VISIONOS_ENABLED)
// Create a full-screen window
CGRect windowBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:windowBounds];
#else
self.window = [[UIWindow alloc] init];
#endif

int err = ios_main(gargc, gargv);
int err = apple_embedded_main(gargc, gargv);

if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
return NO;
}

ViewController *viewController = [[ViewController alloc] init];
GDTViewController *viewController = [[GDTViewController alloc] init];
viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;

Expand Down Expand Up @@ -135,10 +138,10 @@ - (void)onAudioInterruption:(NSNotification *)notification {
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
OS_IOS::get_singleton()->on_focus_out();
OS_AppleEmbedded::get_singleton()->on_focus_out();
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
OS_IOS::get_singleton()->on_focus_in();
OS_AppleEmbedded::get_singleton()->on_focus_in();
}
}
}
Expand All @@ -150,7 +153,7 @@ - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}

- (void)applicationWillTerminate:(UIApplication *)application {
ios_finish();
apple_embedded_finish();
}

// When application goes to background (e.g. user switches to another app or presses Home),
Expand All @@ -164,19 +167,19 @@ - (void)applicationWillTerminate:(UIApplication *)application {
// notification panel by swiping from the upper part of the screen.

- (void)applicationWillResignActive:(UIApplication *)application {
OS_IOS::get_singleton()->on_focus_out();
OS_AppleEmbedded::get_singleton()->on_focus_out();
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
OS_IOS::get_singleton()->on_focus_in();
OS_AppleEmbedded::get_singleton()->on_focus_in();
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
OS_IOS::get_singleton()->on_enter_background();
OS_AppleEmbedded::get_singleton()->on_enter_background();
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
OS_IOS::get_singleton()->on_exit_background();
OS_AppleEmbedded::get_singleton()->on_exit_background();
}

- (void)dealloc {
Expand Down
Loading