From 5416d385d3bc0d0c8cc5b5726adf84e25e49d1e7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 May 2023 22:03:17 +0200 Subject: [PATCH] iOS runtime impl progress --- Source/Engine/Platform/File.h | 4 +- Source/Engine/Platform/FileSystem.h | 2 +- Source/Engine/Platform/Types.h | 8 +-- Source/Engine/Platform/iOS/iOSFile.h | 23 ++++++++ Source/Engine/Platform/iOS/iOSFileSystem.h | 20 +++++++ Source/Engine/Platform/iOS/iOSPlatform.cpp | 44 ++++++++++++++++ Source/Engine/Scripting/Runtime/DotNet.cpp | 61 ++++++++++++++++++++++ Source/Engine/Scripting/Scripting.cpp | 6 +++ 8 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 Source/Engine/Platform/iOS/iOSFile.h create mode 100644 Source/Engine/Platform/iOS/iOSFileSystem.h diff --git a/Source/Engine/Platform/File.h b/Source/Engine/Platform/File.h index 12c772064..607356c8f 100644 --- a/Source/Engine/Platform/File.h +++ b/Source/Engine/Platform/File.h @@ -4,8 +4,10 @@ #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT #include "Win32/Win32File.h" -#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS +#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC #include "Unix/UnixFile.h" +#elif PLATFORM_IOS +#include "iOS/iOSFile.h" #elif PLATFORM_ANDROID #include "Android/AndroidFile.h" #elif PLATFORM_SWITCH diff --git a/Source/Engine/Platform/FileSystem.h b/Source/Engine/Platform/FileSystem.h index 80874a05a..9398ff704 100644 --- a/Source/Engine/Platform/FileSystem.h +++ b/Source/Engine/Platform/FileSystem.h @@ -23,7 +23,7 @@ #elif PLATFORM_MAC #include "Mac/MacFileSystem.h" #elif PLATFORM_IOS -#include "Apple/AppleFileSystem.h" +#include "iOS/iOSFileSystem.h" #else #error Missing File System implementation! #endif diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index 4eff07b18..76135dcaf 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -260,12 +260,12 @@ class UnixCriticalSection; typedef UnixCriticalSection CriticalSection; class UnixConditionVariable; typedef UnixConditionVariable ConditionVariable; -class AppleFileSystem; -typedef AppleFileSystem FileSystem; +class iOSFileSystem; +typedef iOSFileSystem FileSystem; class FileSystemWatcherBase; typedef FileSystemWatcherBase FileSystemWatcher; -class UnixFile; -typedef UnixFile File; +class iOSFile; +typedef iOSFile File; class iOSPlatform; typedef iOSPlatform Platform; class AppleThread; diff --git a/Source/Engine/Platform/iOS/iOSFile.h b/Source/Engine/Platform/iOS/iOSFile.h new file mode 100644 index 000000000..7c7400835 --- /dev/null +++ b/Source/Engine/Platform/iOS/iOSFile.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_IOS + +#include "Engine/Platform/Unix/UnixFile.h" + +/// +/// iOS platform file object implementation. +/// +class FLAXENGINE_API iOSFile : public UnixFile +{ +public: + iOSFile(int32 handle) + : UnixFile(handle) + { + } + + static iOSFile* Open(const StringView& path, FileMode mode, FileAccess access = FileAccess::ReadWrite, FileShare share = FileShare::None); +}; + +#endif diff --git a/Source/Engine/Platform/iOS/iOSFileSystem.h b/Source/Engine/Platform/iOS/iOSFileSystem.h new file mode 100644 index 000000000..60b3ec43a --- /dev/null +++ b/Source/Engine/Platform/iOS/iOSFileSystem.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_IOS + +#include "Engine/Platform/Apple/AppleFileSystem.h" + +/// +/// iOS platform implementation of filesystem service. +/// +class FLAXENGINE_API iOSFileSystem : public AppleFileSystem +{ +public: + static bool FileExists(const StringView& path); + static uint64 GetFileSize(const StringView& path); + static bool IsReadOnly(const StringView& path); +}; + +#endif diff --git a/Source/Engine/Platform/iOS/iOSPlatform.cpp b/Source/Engine/Platform/iOS/iOSPlatform.cpp index 8960ff452..cda8235d0 100644 --- a/Source/Engine/Platform/iOS/iOSPlatform.cpp +++ b/Source/Engine/Platform/iOS/iOSPlatform.cpp @@ -4,6 +4,8 @@ #include "iOSPlatform.h" #include "iOSWindow.h" +#include "iOSFile.h" +#include "iOSFileSystem.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Array.h" @@ -13,12 +15,20 @@ #include "Engine/Platform/Window.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Threading/Threading.h" +#include "Engine/Engine/Engine.h" +#include "Engine/Engine/Globals.h" #include #include int32 Dpi = 96; Guid DeviceId; +// Used by iOS project in XCode to run engine (see main.m) +extern "C" FLAXENGINE_API int FlaxEngineMain() +{ + return Engine::Main(TEXT("")); +} + DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) { NSString* title = (NSString*)AppleUtils::ToString(caption); @@ -38,6 +48,40 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri return DialogResult::OK; } +#define IOS_FALLBACK_PATH(path) (Globals::ProjectFolder / StringUtils::GetFileName(path)) + +iOSFile* iOSFile::Open(const StringView& path, FileMode mode, FileAccess access, FileShare share) +{ + iOSFile* file = (iOSFile*)UnixFile::Open(path, mode, access, share); + if (!file && mode == FileMode::OpenExisting) + { + // Fallback to file placed side-by-side with application + file = (iOSFile*)UnixFile::Open(IOS_FALLBACK_PATH(path), mode, access, share); + } + return file; +} + +bool iOSFileSystem::FileExists(const StringView& path) +{ + return AppleFileSystem::FileExists(path) || AppleFileSystem::FileExists(IOS_FALLBACK_PATH(path)); +} + +uint64 iOSFileSystem::GetFileSize(const StringView& path) +{ + if (AppleFileSystem::FileExists(path)) + return AppleFileSystem::GetFileSize(path); + return AppleFileSystem::GetFileSize(IOS_FALLBACK_PATH(path)); +} + +bool iOSFileSystem::IsReadOnly(const StringView& path) +{ + if (AppleFileSystem::FileExists(path)) + return AppleFileSystem::IsReadOnly(path); + return AppleFileSystem::IsReadOnly(IOS_FALLBACK_PATH(path)); +} + +#undef IOS_FALLBACK_PATH + bool iOSPlatform::Init() { if (ApplePlatform::Init()) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 3ae1e02d5..80f9a801f 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1782,6 +1782,7 @@ static MonoAssembly* OnMonoAssemblyPreloadHook(MonoAssemblyName* aname, char** a } #if 0 + static unsigned char* OnMonoLoadAOT(MonoAssembly* assembly, int size, void* user_data, void** out_handle) { MonoAssemblyName* assemblyName = mono_assembly_get_name(assembly); @@ -1800,6 +1801,57 @@ static void OnMonoFreeAOT(MonoAssembly* assembly, int size, void* user_data, voi LOG(Info, "Free AOT data for C# assembly {0}", String(assemblyNameStr)); #endif } + +#endif + +#if PLATFORM_IOS + +#include "Engine/Engine/Globals.h" +#include +#include + +static void* OnMonoDlFallbackLoad(const char* name, int flags, char** err, void* user_data) +{ + const String fileName = StringUtils::GetFileName(String(name)); +#if DOTNET_HOST_MONO_DEBUG + LOG(Info, "Loading dynamic library {0}", fileName); +#endif + int dlFlags = 0; + if (flags & MONO_DL_GLOBAL && !(flags & MONO_DL_LOCAL)) + dlFlags |= RTLD_GLOBAL; + else + dlFlags |= RTLD_LOCAL; + if (flags & MONO_DL_LAZY) + dlFlags |= RTLD_LAZY; + else + dlFlags |= RTLD_NOW; + void* result = dlopen(name, dlFlags); + if (!result) + { + // Try Frameworks location on iOS + String path = Globals::ProjectFolder / TEXT("Frameworks") / fileName; + if (!path.EndsWith(TEXT(".dylib"))) + path += TEXT(".dylib"); + result = dlopen(StringAsANSI<>(*path).Get(), dlFlags); + if (!result) + { + LOG(Error, "Failed to load dynamic libary {0}", String(name)); + } + } + return result; +} + +static void* OnMonoDlFallbackSymbol(void* handle, const char* name, char** err, void* user_data) +{ + return dlsym(handle, name); +} + +static void* OnMonoDlFallbackClose(void* handle, void* user_data) +{ + dlclose(handle); + return 0; +} + #endif bool InitHostfxr() @@ -1815,6 +1867,12 @@ bool InitHostfxr() // Enable AOT mode (per-platform) mono_jit_set_aot_mode(USE_MONO_AOT_MODE); #endif + + // Platform-specific setup +#if PLATFORM_IOS + setenv("MONO_AOT_MODE", "aot", 1); + setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1); +#endif #ifdef USE_MONO_AOT_MODULE // Load AOT module @@ -1916,6 +1974,9 @@ bool InitHostfxr() #if 0 mono_install_load_aot_data_hook(OnMonoLoadAOT, OnMonoFreeAOT, nullptr); #endif +#if PLATFORM_IOS + mono_dl_fallback_register(OnMonoDlFallbackLoad, OnMonoDlFallbackSymbol, OnMonoDlFallbackClose, nullptr); +#endif // Init managed runtime #if PLATFORM_ANDROID || PLATFORM_IOS diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 8796c7587..1159ae823 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -351,6 +351,12 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde { nativePath = String(StringUtils::GetDirectoryName(Platform::GetExecutableFilePath())) / StringUtils::GetFileName(nativePath); } +#elif PLATFORM_IOS + // iOS uses Frameworks folder with native binaries + if (!FileSystem::FileExists(nativePath)) + { + nativePath = Globals::ProjectFolder / TEXT("Frameworks") / StringUtils::GetFileName(nativePath); + } #endif auto library = Platform::LoadLibrary(nativePath.Get()); if (!library)