mirror of
https://github.com/xenios-jp/XeniOS.git
synced 2026-07-11 15:19:09 -07:00
[iOS] Add XeniOS app layer
This commit is contained in:
@@ -88,6 +88,8 @@ node_modules/
|
||||
/build-vs/
|
||||
/build-arm64/
|
||||
/build-x64/
|
||||
/build-ios/
|
||||
/build-ios-xcode/
|
||||
|
||||
# CMake user-specific overrides + compile commands export
|
||||
CMakeUserPresets.json
|
||||
@@ -105,6 +107,7 @@ CMakeUserPresets.json
|
||||
/third_party/binutils/bin/
|
||||
/third_party/binutils/powerpc-none-elf/
|
||||
/third_party/binutils/share/
|
||||
/third_party/MoltenVK/
|
||||
/third_party/vasm/
|
||||
/tools/shader-playground/*.dll
|
||||
/profile_print_times.py
|
||||
|
||||
+41
-16
@@ -1,15 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
|
||||
|
||||
# Must be set before project() so the toolchain bakes -mmacosx-version-min
|
||||
# into every compile/link; setting it later only updates the variable.
|
||||
if(APPLE)
|
||||
# Keep Apple platform selection explicit. CMake sets APPLE for both macOS and
|
||||
# iOS, but the two targets need different bundles, frameworks and dependencies.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
set(XE_PLATFORM_IOS TRUE)
|
||||
set(XE_PLATFORM_MACOS FALSE)
|
||||
elseif(APPLE)
|
||||
set(XE_PLATFORM_IOS FALSE)
|
||||
set(XE_PLATFORM_MACOS TRUE)
|
||||
else()
|
||||
set(XE_PLATFORM_IOS FALSE)
|
||||
set(XE_PLATFORM_MACOS FALSE)
|
||||
endif()
|
||||
|
||||
# Must be set before project() so the toolchain bakes the deployment target into
|
||||
# every compile/link; setting it later only updates the variable.
|
||||
if(XE_PLATFORM_IOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "18.0" CACHE STRING "Minimum iOS version")
|
||||
elseif(XE_PLATFORM_MACOS)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS version")
|
||||
endif()
|
||||
|
||||
# Suppress macOS ranlib warnings for empty object files. wxWidgets's vendored
|
||||
# libpng compiles Intel/MIPS/PowerPC sources that are empty on ARM64.
|
||||
if(APPLE)
|
||||
if(APPLE OR XE_PLATFORM_IOS)
|
||||
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
|
||||
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
|
||||
set(CMAKE_OBJC_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
|
||||
@@ -35,8 +50,8 @@ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
|
||||
|
||||
# Detect target architecture.
|
||||
# - VS generator: CMAKE_GENERATOR_PLATFORM (from -A flag) takes priority.
|
||||
# - macOS: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds
|
||||
# would need both backends compiled, which we don't support).
|
||||
# - Apple: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds would
|
||||
# need both backends compiled, which we don't support).
|
||||
# - Ninja/Makefiles: CMAKE_SYSTEM_PROCESSOR (set via -DCMAKE_SYSTEM_NAME
|
||||
# + -DCMAKE_SYSTEM_PROCESSOR for cross-compile, or auto-detected natively).
|
||||
if(CMAKE_GENERATOR_PLATFORM)
|
||||
@@ -82,6 +97,7 @@ option(XENIA_ENABLE_PROFILER "Enable profiler (UI is Debug-only)" OFF)
|
||||
option(XENIA_ENABLE_ITRACE "Enable JIT per-instruction tracing to the log (slow)" OFF)
|
||||
option(XENIA_ENABLE_DTRACE "Enable JIT per-operation data tracing to the log (very slow)" OFF)
|
||||
option(XENIA_ENABLE_FTRACE "Enable JIT per-function-call tracing to the log" OFF)
|
||||
option(XENIA_ENABLE_IOS_MOLTENVK "Enable the optional MoltenVK backend in iOS builds" OFF)
|
||||
|
||||
if(XENIA_ENABLE_PROFILER)
|
||||
# UI is Debug-only — shutdown dump spams the log otherwise.
|
||||
@@ -113,7 +129,9 @@ set(CMAKE_STATIC_LINKER_FLAGS_CHECKED "${CMAKE_STATIC_LINKER_FLAGS_DEBUG}" CACHE
|
||||
|
||||
if(WIN32)
|
||||
set(XE_PLATFORM_NAME "Windows")
|
||||
elseif(APPLE)
|
||||
elseif(XE_PLATFORM_IOS)
|
||||
set(XE_PLATFORM_NAME "iOS")
|
||||
elseif(XE_PLATFORM_MACOS)
|
||||
set(XE_PLATFORM_NAME "macOS")
|
||||
else()
|
||||
set(XE_PLATFORM_NAME "Linux")
|
||||
@@ -327,7 +345,7 @@ else()
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
# macOS-specific settings
|
||||
# Apple-specific settings.
|
||||
add_compile_options(
|
||||
-Wno-unknown-warning-option
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-shorten-64-to-32>
|
||||
@@ -336,20 +354,27 @@ else()
|
||||
-fno-common
|
||||
)
|
||||
add_link_options(-Wl,-no_warn_duplicate_libraries)
|
||||
if(XE_TARGET_X86_64)
|
||||
if(XE_PLATFORM_MACOS AND XE_TARGET_X86_64)
|
||||
# macOS 64-bit binaries default to a 4GB __PAGEZERO, which makes every
|
||||
# address below 4GB unmapped. The x64 backend places its constant
|
||||
# table (PlaceConstData) sub-2GB and its guest trampolines sub-4GB,
|
||||
# so we shrink __PAGEZERO to one page to free up that range.
|
||||
add_link_options(-Wl,-pagezero_size,0x1000)
|
||||
endif()
|
||||
link_libraries(
|
||||
"-framework CoreFoundation"
|
||||
"-framework Foundation"
|
||||
"-framework IOKit"
|
||||
dl
|
||||
pthread
|
||||
)
|
||||
if(XE_PLATFORM_MACOS)
|
||||
link_libraries(
|
||||
"-framework CoreFoundation"
|
||||
"-framework Foundation"
|
||||
"-framework IOKit"
|
||||
dl
|
||||
pthread
|
||||
)
|
||||
elseif(XE_PLATFORM_IOS)
|
||||
link_libraries(
|
||||
"-framework CoreFoundation"
|
||||
"-framework Foundation"
|
||||
)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0)
|
||||
|
||||
@@ -1,20 +1,100 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/xenia-canary/xenia-canary/tree/canary_experimental/assets/icon">
|
||||
<img height="256px" src="https://raw.githubusercontent.com/xenia-canary/xenia/master/assets/icon/256.png" />
|
||||
</a>
|
||||
<img height="256px" src="assets/apple/xenios-readme-icon.png" alt="XeniOS app icon" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Xenia Edge - Xbox 360 Emulator</h1>
|
||||
<h1 align="center">XeniOS - Xbox 360 Emulator</h1>
|
||||
|
||||
Xenia Edge is yet another experimental fork of the Xenia emulator, originally based on [Xenia Canary](https://github.com/xenia-canary/xenia-canary). The focus is
|
||||
on faster iteration, higher default game compatibility, usability and platform support.
|
||||
XeniOS is an experimental Apple-focused fork of Xenia, currently based on
|
||||
[Xenia Edge](https://github.com/has207/xenia-edge). It exists as a fast-moving
|
||||
place to develop, test, and ship iOS and macOS work while also carrying
|
||||
platform changes that benefit ARM64 Windows, Linux, and Android. Relevant
|
||||
improvements are intended to flow back upstream over time.
|
||||
|
||||
## Status
|
||||
<p align="center">
|
||||
<a href="https://xenios.jp">Website</a> ◦
|
||||
<a href="https://github.com/xenios-jp/XeniOS/releases">Releases</a> ◦
|
||||
<a href="https://xenios.jp/docs">Docs</a> ◦
|
||||
<a href="https://xenios.jp/faq">FAQ</a> ◦
|
||||
<a href="https://xenios.jp/compatibility">Compatibility</a> ◦
|
||||
<a href="https://discord.gg/QwcTtNKTGf">Discord</a> ◦
|
||||
<a href="https://github.com/xenios-jp/XeniOS/issues">Issues</a>
|
||||
</p>
|
||||
|
||||
Build (Windows / Linux / macOS arm64 / macOS x86_64): [](https://github.com/has207/xenia-edge/actions/workflows/CI.yml) [](https://app.codacy.com/gh/has207/xenia-edge/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
||||
## Current Focus
|
||||
|
||||
Releases
|
||||
--------
|
||||
[Latest](https://github.com/has207/xenia-edge/releases/latest) ◦ [All](https://github.com/has207/xenia-edge/releases)
|
||||
- iOS
|
||||
- macOS (Apple Silicon)
|
||||
- macOS (Intel)
|
||||
|
||||
Current published releases focus on iOS and macOS.
|
||||
For Windows or Linux builds, use [Xenia Edge](https://github.com/has207/xenia-edge) or [Xenia Canary](https://github.com/xenia-canary/xenia-canary).
|
||||
|
||||
## Why This Fork Exists
|
||||
|
||||
Xenia development is relatively thinly staffed right now, and upstream is not
|
||||
set up for fast iteration on Apple-specific packaging, documentation, release
|
||||
flow, and user experience. XeniOS exists so that work can move faster in a
|
||||
repository where the full product experience can be shaped directly, from the
|
||||
app itself to releases, docs, compatibility reporting, and the public website
|
||||
at [xenios.jp](https://xenios.jp).
|
||||
|
||||
The goal is not to keep good work siloed here forever. The goal is to iterate
|
||||
quickly, build a more polished user-facing experience for Apple platforms, and
|
||||
then contribute the useful technical improvements back upstream into the
|
||||
broader Xenia community, especially
|
||||
[Xenia Canary](https://github.com/xenia-canary/xenia-canary).
|
||||
|
||||
## Downloads
|
||||
|
||||
Download XeniOS from
|
||||
[GitHub Releases](https://github.com/xenios-jp/XeniOS/releases).
|
||||
|
||||
- [Latest GitHub release](https://github.com/xenios-jp/XeniOS/releases/latest)
|
||||
- [All GitHub releases](https://github.com/xenios-jp/XeniOS/releases)
|
||||
|
||||
## Quickstart
|
||||
|
||||
Start with the public docs at [xenios.jp/docs](https://xenios.jp/docs).
|
||||
|
||||
## FAQ
|
||||
|
||||
See the public FAQ at [xenios.jp/faq](https://xenios.jp/faq).
|
||||
|
||||
## Game Compatibility
|
||||
|
||||
Browse currently tracked games on
|
||||
[xenios.jp/compatibility](https://xenios.jp/compatibility).
|
||||
|
||||
To file a compatibility report, use the
|
||||
[GitHub compatibility tracker](https://github.com/xenios-jp/game-compatibility/issues/new/choose).
|
||||
|
||||
## Building
|
||||
|
||||
See [building.md](docs/building.md) for setup and information about the
|
||||
`xb` script. When writing code, check the [style guide](docs/style_guide.md)
|
||||
and be sure to run clang-format!
|
||||
|
||||
## Contributors Wanted!
|
||||
|
||||
Have some spare time, know advanced C++, and want to write an emulator?
|
||||
Contribute! There's a ton of work that needs to be done, a lot of which
|
||||
is wide open greenfield fun.
|
||||
|
||||
**For general rules and guidelines please see [CONTRIBUTING.md](.github/CONTRIBUTING.md).**
|
||||
|
||||
Fixes and optimizations are always welcome, especially around Apple platform
|
||||
performance, UI polish, compatibility coverage, packaging, and tooling.
|
||||
|
||||
Start with the
|
||||
[XeniOS issue tracker](https://github.com/xenios-jp/XeniOS/issues),
|
||||
join the [XeniOS Discord](https://discord.gg/QwcTtNKTGf), check
|
||||
[CONTRIBUTING.md](.github/CONTRIBUTING.md), and coordinate before starting
|
||||
larger work.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
The goal of this project is to experiment, research, and educate on the topic
|
||||
of emulation of modern devices and operating systems. **It is not for enabling
|
||||
illegal activity**. All information is obtained via reverse engineering of
|
||||
legally purchased devices and games and information made public on the internet
|
||||
(you'd be surprised what's indexed on Google...).
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
include(CMakeParseArguments)
|
||||
|
||||
set(XE_PLATFORM_SUFFIXES
|
||||
_win _linux _posix _gnulinux _x11 _gtk _android _mac _amd64 _x64 _arm64
|
||||
_win _linux _posix _gnulinux _x11 _gtk _android _mac _ios _amd64 _x64 _arm64
|
||||
)
|
||||
|
||||
# xe_platform_sources(target base_path [RECURSIVE])
|
||||
@@ -55,7 +55,14 @@ function(xe_platform_sources target base_path)
|
||||
"${base_path}/*_win.h"
|
||||
"${base_path}/*_win.cc"
|
||||
)
|
||||
elseif(APPLE)
|
||||
elseif(XE_PLATFORM_IOS)
|
||||
file(${glob_mode} _plat_sources
|
||||
"${base_path}/*_posix.h"
|
||||
"${base_path}/*_posix.cc"
|
||||
"${base_path}/*_ios.h"
|
||||
"${base_path}/*_ios.cc"
|
||||
)
|
||||
elseif(XE_PLATFORM_MACOS)
|
||||
file(${glob_mode} _plat_sources
|
||||
"${base_path}/*_posix.h"
|
||||
"${base_path}/*_posix.cc"
|
||||
@@ -106,6 +113,8 @@ function(xe_target_defaults target)
|
||||
)
|
||||
if(MSVC)
|
||||
target_compile_options(${target} PRIVATE /WX)
|
||||
elseif(XCODE AND XE_PLATFORM_IOS)
|
||||
target_compile_options(${target} PRIVATE -w)
|
||||
elseif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${target} PRIVATE -Werror)
|
||||
endif()
|
||||
@@ -327,6 +336,13 @@ function(xe_shader_rules_metal target shader_dir)
|
||||
set(_generated_root "${PROJECT_BINARY_DIR}/generated")
|
||||
set(_bytecode_dir "${_generated_root}/${_rel_dir}/bytecode/metal")
|
||||
set(_valid_stages vs ps cs)
|
||||
set(_metal_args --msl)
|
||||
if(XE_PLATFORM_IOS)
|
||||
list(APPEND _metal_args
|
||||
--metal-sdk iphoneos
|
||||
--metal-std ios-metal2.3
|
||||
--metal-min-version-flag "-miphoneos-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||
endif()
|
||||
set(_outputs)
|
||||
file(MAKE_DIRECTORY "${_bytecode_dir}")
|
||||
foreach(src ${_sources})
|
||||
@@ -350,7 +366,7 @@ function(xe_shader_rules_metal target shader_dir)
|
||||
list(APPEND _outputs "${_out}")
|
||||
add_custom_command(
|
||||
OUTPUT "${_out}"
|
||||
COMMAND $<TARGET_FILE:xenia-shader-cc> --msl --depfile "${_dep}"
|
||||
COMMAND $<TARGET_FILE:xenia-shader-cc> ${_metal_args} --depfile "${_dep}"
|
||||
"${src}" "${_out}"
|
||||
DEPENDS "${src}" xenia-shader-cc
|
||||
DEPFILE "${_dep}"
|
||||
|
||||
@@ -28,19 +28,25 @@ if(APPLE)
|
||||
endif()
|
||||
add_subdirectory(helper/sdl)
|
||||
add_subdirectory(hid)
|
||||
add_subdirectory(hid/touch)
|
||||
add_subdirectory(hid/nop)
|
||||
add_subdirectory(hid/sdl)
|
||||
add_subdirectory(hid/portal)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(patcher)
|
||||
add_subdirectory(ui)
|
||||
if(XE_PLATFORM_IOS)
|
||||
add_subdirectory(ui/ios)
|
||||
endif()
|
||||
add_subdirectory(ui/vulkan)
|
||||
if(APPLE)
|
||||
add_subdirectory(ui/metal)
|
||||
endif()
|
||||
add_subdirectory(vfs)
|
||||
add_subdirectory(debug/ui)
|
||||
add_subdirectory(app/discord)
|
||||
if(NOT XE_PLATFORM_IOS)
|
||||
add_subdirectory(debug/ui)
|
||||
add_subdirectory(app/discord)
|
||||
endif()
|
||||
add_subdirectory(app)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -1,3 +1,169 @@
|
||||
if(XE_PLATFORM_IOS)
|
||||
set(_xe_ios_entitlements "${PROJECT_SOURCE_DIR}/xenia_ios.entitlements")
|
||||
add_executable(xenia-app MACOSX_BUNDLE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xenia_main_ios.mm
|
||||
${PROJECT_SOURCE_DIR}/src/xenia/ui/ios/app/windowed_app_main_ios.mm
|
||||
)
|
||||
set_target_properties(xenia-app PROPERTIES
|
||||
OUTPUT_NAME "XeniOS"
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info_ios.plist"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "XeniOS"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.xenios"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "1"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.xenios"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS
|
||||
"${_xe_ios_entitlements}")
|
||||
target_sources(xenia-app PRIVATE "${_xe_ios_entitlements}")
|
||||
target_compile_definitions(xenia-app PRIVATE
|
||||
XBYAK_NO_OP_NAMES
|
||||
XBYAK_ENABLE_OMITTED_OPERAND
|
||||
)
|
||||
target_link_libraries(xenia-app PRIVATE
|
||||
xenia-apu
|
||||
xenia-apu-nop
|
||||
xenia-apu-sdl
|
||||
xenia-base
|
||||
xenia-core
|
||||
xenia-cpu
|
||||
xenia-cpu-backend-a64
|
||||
xenia-gpu
|
||||
xenia-gpu-metal
|
||||
xenia-helper-sdl
|
||||
xenia-hid
|
||||
xenia-hid-nop
|
||||
xenia-hid-sdl
|
||||
xenia-hid-touch
|
||||
xenia-kernel
|
||||
xenia-patcher
|
||||
xenia-ui
|
||||
xenia-ui-ios
|
||||
xenia-ui-metal
|
||||
xenia-vfs
|
||||
aes_128
|
||||
capstone
|
||||
dxbc
|
||||
fmt
|
||||
imgui
|
||||
libavcodec
|
||||
libavformat
|
||||
libavutil
|
||||
mspack
|
||||
snappy
|
||||
xxhash
|
||||
"-framework AVFoundation"
|
||||
"-framework AudioToolbox"
|
||||
"-framework CoreFoundation"
|
||||
"-framework Foundation"
|
||||
"-framework GameController"
|
||||
"-framework Metal"
|
||||
"-framework MetalKit"
|
||||
"-framework QuartzCore"
|
||||
"-framework Security"
|
||||
"-framework UIKit"
|
||||
"-framework UniformTypeIdentifiers"
|
||||
)
|
||||
if(XENIA_ENABLE_IOS_MOLTENVK)
|
||||
target_link_libraries(xenia-app PRIVATE
|
||||
xenia-gpu-vulkan
|
||||
xenia-ui-vulkan
|
||||
xenia-third-party-moltenvk
|
||||
glslang-spirv
|
||||
)
|
||||
endif()
|
||||
target_link_options(xenia-app PRIVATE
|
||||
"-Wl,-u,___clear_cache"
|
||||
"-Wl,-rpath,@executable_path/Frameworks"
|
||||
"-Wl,-rpath,@loader_path/Frameworks"
|
||||
)
|
||||
set(_xe_ios_embedded_dylibs)
|
||||
if(XENIA_ENABLE_IOS_MOLTENVK)
|
||||
set(_xe_moltenvk_ios_framework
|
||||
"${PROJECT_SOURCE_DIR}/third_party/MoltenVK/ios/MoltenVK.framework")
|
||||
if(XCODE)
|
||||
list(APPEND _xe_ios_embedded_dylibs "${_xe_moltenvk_ios_framework}")
|
||||
else()
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||
"${_xe_moltenvk_ios_framework}"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks/MoltenVK.framework"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endif()
|
||||
foreach(_xe_app_dylib xenia-third-party-metal-shader-converter
|
||||
xenia-third-party-dxilconv)
|
||||
if(TARGET ${_xe_app_dylib})
|
||||
if(XCODE)
|
||||
get_target_property(_xe_app_dylib_path ${_xe_app_dylib}
|
||||
IMPORTED_LOCATION_RELEASE)
|
||||
if(NOT _xe_app_dylib_path)
|
||||
get_target_property(_xe_app_dylib_path ${_xe_app_dylib}
|
||||
IMPORTED_LOCATION)
|
||||
endif()
|
||||
if(_xe_app_dylib_path)
|
||||
list(APPEND _xe_ios_embedded_dylibs "${_xe_app_dylib_path}")
|
||||
endif()
|
||||
else()
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"$<TARGET_FILE:${_xe_app_dylib}>"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks/$<TARGET_FILE_NAME:${_xe_app_dylib}>"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
if(XCODE AND _xe_ios_embedded_dylibs)
|
||||
set_target_properties(xenia-app PROPERTIES
|
||||
XCODE_EMBED_FRAMEWORKS "${_xe_ios_embedded_dylibs}"
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES)
|
||||
endif()
|
||||
target_include_directories(xenia-app PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/third_party/rapidjson/include"
|
||||
)
|
||||
xe_embed_binary_assets(xenia-app "${PROJECT_SOURCE_DIR}/assets/icons" icons)
|
||||
xe_embed_compressed_bundle(xenia-app
|
||||
"${PROJECT_SOURCE_DIR}/build/data_repos/xenia-manager-database/data/game-compatibility"
|
||||
game_compat)
|
||||
set(_xe_ios_ad_hoc_sign_command
|
||||
codesign --force --deep --sign -
|
||||
--entitlements "${_xe_ios_entitlements}"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>")
|
||||
if(NOT XCODE)
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND ${_xe_ios_ad_hoc_sign_command}
|
||||
VERBATIM)
|
||||
endif()
|
||||
set(_xe_ios_package_command
|
||||
/bin/bash "${PROJECT_SOURCE_DIR}/tools/package_ios_ipa.sh"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>"
|
||||
"${_xe_ios_entitlements}"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.ipa"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.requested-entitlements.plist"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.signed-entitlements.plist")
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND ${_xe_ios_package_command}
|
||||
VERBATIM)
|
||||
if(XCODE)
|
||||
add_custom_target(xenia-app-ios-package
|
||||
COMMAND ${_xe_ios_package_command}
|
||||
DEPENDS xenia-app
|
||||
VERBATIM)
|
||||
else()
|
||||
add_custom_target(xenia-app-ios-package
|
||||
COMMAND ${_xe_ios_ad_hoc_sign_command}
|
||||
COMMAND ${_xe_ios_package_command}
|
||||
DEPENDS xenia-app
|
||||
VERBATIM)
|
||||
endif()
|
||||
xe_target_defaults(xenia-app)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_executable(xenia-app WIN32)
|
||||
elseif(APPLE)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Xbox 360 Disc Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.iso-image</string>
|
||||
<string>public.disk-image</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>xex</string>
|
||||
<string>zar</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Xbox 360 Executable</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>jp.xenios.xenios.launch</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>xenios</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>stikjit</string>
|
||||
</array>
|
||||
<key>LSSupportsGameMode</key>
|
||||
<true/>
|
||||
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>XeniaSceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>metal</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "third_party/stb/stb_image.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/vfs/iso_metadata.h"
|
||||
#include "xenia/vfs/stfs_metadata.h"
|
||||
#include "xenia/vfs/xex_metadata.h"
|
||||
@@ -173,6 +174,10 @@ size_t DirectoryScanner::CountResultsWithTitleId(
|
||||
}
|
||||
|
||||
void DirectoryScanner::Run(std::filesystem::path root) {
|
||||
#if XE_PLATFORM_IOS
|
||||
xe::threading::set_name("Directory Scanner");
|
||||
xe::threading::set_current_thread_qos(xe::threading::ThreadQoS::kUtility);
|
||||
#endif
|
||||
XELOGI("DirectoryScanner: scan start, root='{}'", xe::path_to_utf8(root));
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::is_directory(root, ec) || ec) {
|
||||
@@ -219,6 +224,10 @@ void DirectoryScanner::Run(std::filesystem::path root) {
|
||||
}
|
||||
|
||||
void DirectoryScanner::WorkerLoop() {
|
||||
#if XE_PLATFORM_IOS
|
||||
xe::threading::set_name("Directory Scanner Worker");
|
||||
xe::threading::set_current_thread_qos(xe::threading::ThreadQoS::kUtility);
|
||||
#endif
|
||||
while (true) {
|
||||
std::filesystem::path dir;
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "xenia/apu/audio_system.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/apu/apu_flags.h"
|
||||
#include "xenia/apu/audio_driver.h"
|
||||
#include "xenia/apu/xma_decoder.h"
|
||||
@@ -35,7 +37,21 @@
|
||||
// and let the normal AudioSystem handling take it, to prevent duplicate
|
||||
// implementations. They can be found in xboxkrnl_audio_xma.cc
|
||||
|
||||
DEFINE_uint32(apu_max_queued_frames, 8,
|
||||
namespace {
|
||||
#if XE_PLATFORM_IOS
|
||||
constexpr uint32_t kApuQueuedFramesDefault = 32;
|
||||
constexpr uint32_t kApuQueuedFramesMinimum = 32;
|
||||
|
||||
bool IsTitleStopRequested(xe::cpu::Processor* processor) {
|
||||
return processor && processor->title_stop_requested_ios();
|
||||
}
|
||||
#else
|
||||
constexpr uint32_t kApuQueuedFramesDefault = 8;
|
||||
constexpr uint32_t kApuQueuedFramesMinimum = 4;
|
||||
#endif // XE_PLATFORM_IOS
|
||||
} // namespace
|
||||
|
||||
DEFINE_uint32(apu_max_queued_frames, kApuQueuedFramesDefault,
|
||||
"Allows changing max buffered audio frames to reduce audio "
|
||||
"delay. Lowering this value might cause performance issues. "
|
||||
"Value range: [4-64]",
|
||||
@@ -97,11 +113,26 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) {
|
||||
}
|
||||
|
||||
void AudioSystem::WorkerThreadMain() {
|
||||
#if XE_PLATFORM_IOS
|
||||
if (xe::threading::set_current_thread_qos(
|
||||
xe::threading::ThreadQoS::kUserInteractive)) {
|
||||
XELOGI("iOS: Audio Worker QoS set to user-interactive");
|
||||
} else {
|
||||
XELOGW("iOS: Audio Worker QoS request failed");
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
|
||||
// Initialize driver and ringbuffer.
|
||||
Initialize();
|
||||
|
||||
// Main run loop.
|
||||
while (worker_running_) {
|
||||
#if XE_PLATFORM_IOS
|
||||
if (IsTitleStopRequested(processor_)) {
|
||||
break;
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
|
||||
// These handles signify the number of submitted samples. Once we reach
|
||||
// 64 samples, we wait until our audio backend releases a semaphore
|
||||
// (signaling a sample has finished playing)
|
||||
@@ -112,10 +143,14 @@ void AudioSystem::WorkerThreadMain() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!worker_running_) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.first == threading::WaitResult::kSuccess &&
|
||||
result.second == kMaximumClientCount) {
|
||||
// Shutdown event signaled.
|
||||
if (paused_) {
|
||||
if (paused_.load(std::memory_order_acquire)) {
|
||||
pause_fence_.Signal();
|
||||
threading::Wait(resume_event_.get(), false);
|
||||
}
|
||||
@@ -141,6 +176,12 @@ void AudioSystem::WorkerThreadMain() {
|
||||
}
|
||||
}
|
||||
|
||||
#if XE_PLATFORM_IOS
|
||||
if (IsTitleStopRequested(processor_)) {
|
||||
break;
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
|
||||
if (client_callback) {
|
||||
SCOPE_profile_cpu_i("apu", "xe::apu::AudioSystem->client_callback");
|
||||
uint64_t args[] = {client_callback_arg};
|
||||
@@ -148,6 +189,12 @@ void AudioSystem::WorkerThreadMain() {
|
||||
args, xe::countof(args));
|
||||
}
|
||||
|
||||
#if XE_PLATFORM_IOS
|
||||
if (IsTitleStopRequested(processor_)) {
|
||||
break;
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
|
||||
pumped = true;
|
||||
}
|
||||
|
||||
@@ -181,6 +228,12 @@ void AudioSystem::Initialize() {}
|
||||
void AudioSystem::Shutdown() {
|
||||
worker_running_ = false;
|
||||
shutdown_event_->Set();
|
||||
#if XE_PLATFORM_IOS
|
||||
resume_event_->Set();
|
||||
for (size_t i = 0; i < kMaximumClientCount; ++i) {
|
||||
client_semaphores_[i]->Release(1, nullptr);
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
if (worker_thread_) {
|
||||
worker_thread_->Wait(0, 0, 0, nullptr);
|
||||
worker_thread_.reset();
|
||||
@@ -411,10 +464,9 @@ bool AudioSystem::Restore(ByteStream* stream) {
|
||||
}
|
||||
|
||||
void AudioSystem::Pause() {
|
||||
if (paused_) {
|
||||
if (paused_.exchange(true, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
paused_ = true;
|
||||
|
||||
// Kind of a hack, but it works.
|
||||
shutdown_event_->Set();
|
||||
@@ -424,10 +476,9 @@ void AudioSystem::Pause() {
|
||||
}
|
||||
|
||||
void AudioSystem::Resume() {
|
||||
if (!paused_) {
|
||||
if (!paused_.exchange(false, std::memory_order_acq_rel)) {
|
||||
return;
|
||||
}
|
||||
paused_ = false;
|
||||
|
||||
resume_event_->Set();
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class AudioSystem {
|
||||
bool Save(ByteStream* stream);
|
||||
bool Restore(ByteStream* stream);
|
||||
|
||||
bool is_paused() const { return paused_; }
|
||||
bool is_paused() const { return paused_.load(std::memory_order_acquire); }
|
||||
void Pause();
|
||||
void Resume();
|
||||
|
||||
@@ -116,7 +116,7 @@ class AudioSystem {
|
||||
std::unique_ptr<xe::threading::Event> shutdown_event_;
|
||||
xe::threading::WaitHandle* wait_handles_[kMaximumClientCount + 1];
|
||||
|
||||
bool paused_ = false;
|
||||
std::atomic<bool> paused_ = false;
|
||||
threading::Fence pause_fence_;
|
||||
std::unique_ptr<threading::Event> resume_event_;
|
||||
};
|
||||
|
||||
@@ -35,8 +35,8 @@ static void _generic_sequential_6_BE_to_interleaved_6_LE(
|
||||
}
|
||||
}
|
||||
}
|
||||
#if XE_COMPILER_CLANG_CL != 1 && !XE_PLATFORM_LINUX && !XE_PLATFORM_MAC
|
||||
// load_be_u32 unavailable on clang-cl, clang on Linux, or clang on macOS
|
||||
#if XE_COMPILER_CLANG_CL != 1 && !XE_PLATFORM_LINUX && !XE_PLATFORM_APPLE
|
||||
// load_be_u32 unavailable on clang-cl, clang on Linux, or clang on Apple
|
||||
XE_NOINLINE
|
||||
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
|
||||
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
||||
|
||||
@@ -22,6 +22,20 @@ namespace xe {
|
||||
namespace apu {
|
||||
namespace sdl {
|
||||
|
||||
namespace {
|
||||
|
||||
bool ShouldReportUnderrun(uint64_t count, uint64_t last_reported_count) {
|
||||
const uint64_t next_report =
|
||||
last_reported_count == 0 ? 1 : last_reported_count * 2;
|
||||
return count >= next_report;
|
||||
}
|
||||
|
||||
bool ShouldReportAudioStats(uint64_t submitted_count) {
|
||||
return (submitted_count & 1023) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SDLAudioDriver::SDLAudioDriver(xe::threading::Semaphore* semaphore,
|
||||
uint32_t frequency, uint32_t channels,
|
||||
bool need_format_conversion)
|
||||
@@ -115,7 +129,57 @@ void SDLAudioDriver::SubmitFrame(float* frame) {
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(frames_mutex_);
|
||||
frames_queued_.push(output_frame);
|
||||
const size_t queue_depth = frames_queued_.size();
|
||||
size_t previous_max = max_queue_depth_.load(std::memory_order_relaxed);
|
||||
while (queue_depth > previous_max &&
|
||||
!max_queue_depth_.compare_exchange_weak(previous_max, queue_depth,
|
||||
std::memory_order_relaxed,
|
||||
std::memory_order_relaxed)) {
|
||||
}
|
||||
if (queue_depth <= 2) {
|
||||
low_queue_count_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
const uint64_t submitted =
|
||||
frames_submitted_.fetch_add(1, std::memory_order_relaxed) + 1;
|
||||
|
||||
const uint64_t underruns = underrun_count_.load(std::memory_order_relaxed);
|
||||
uint64_t last_reported =
|
||||
last_reported_underrun_count_.load(std::memory_order_relaxed);
|
||||
if (underruns != 0 && ShouldReportUnderrun(underruns, last_reported) &&
|
||||
last_reported_underrun_count_.compare_exchange_strong(
|
||||
last_reported, underruns, std::memory_order_relaxed,
|
||||
std::memory_order_relaxed)) {
|
||||
XELOGW(
|
||||
"SDL audio underrun: count={}, submitted={}, played={}, "
|
||||
"max_queue_depth={}",
|
||||
underruns, frames_submitted_.load(std::memory_order_relaxed),
|
||||
frames_played_.load(std::memory_order_relaxed),
|
||||
max_queue_depth_.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
#if XE_PLATFORM_IOS
|
||||
const uint64_t low_queue = low_queue_count_.load(std::memory_order_relaxed);
|
||||
uint64_t last_low_queue =
|
||||
last_reported_low_queue_count_.load(std::memory_order_relaxed);
|
||||
if (low_queue != 0 && ShouldReportUnderrun(low_queue, last_low_queue) &&
|
||||
last_reported_low_queue_count_.compare_exchange_strong(
|
||||
last_low_queue, low_queue, std::memory_order_relaxed,
|
||||
std::memory_order_relaxed)) {
|
||||
XELOGW(
|
||||
"SDL audio queue running low: count={}, submitted={}, played={}, "
|
||||
"underruns={}, max_queue_depth={}",
|
||||
low_queue, submitted, frames_played_.load(std::memory_order_relaxed),
|
||||
underruns, max_queue_depth_.load(std::memory_order_relaxed));
|
||||
}
|
||||
if (ShouldReportAudioStats(submitted)) {
|
||||
XELOGI(
|
||||
"SDL audio stats: submitted={}, played={}, underruns={}, low_queue={}, "
|
||||
"max_queue_depth={}",
|
||||
submitted, frames_played_.load(std::memory_order_relaxed), underruns,
|
||||
low_queue, max_queue_depth_.load(std::memory_order_relaxed));
|
||||
}
|
||||
#endif // XE_PLATFORM_IOS
|
||||
}
|
||||
|
||||
void SDLAudioDriver::Pause() {
|
||||
@@ -169,6 +233,10 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
|
||||
{
|
||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||
if (driver->frames_queued_.empty()) {
|
||||
driver->underrun_count_.fetch_add(1, std::memory_order_relaxed);
|
||||
if (driver->semaphore_) {
|
||||
driver->semaphore_->Release(1, nullptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
buffer = driver->frames_queued_.front();
|
||||
@@ -213,8 +281,11 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
|
||||
driver->frames_unused_.push(buffer);
|
||||
}
|
||||
|
||||
auto ret = driver->semaphore_->Release(1, nullptr);
|
||||
assert_true(ret);
|
||||
driver->frames_played_.fetch_add(1, std::memory_order_relaxed);
|
||||
if (driver->semaphore_) {
|
||||
auto ret = driver->semaphore_->Release(1, nullptr);
|
||||
assert_true(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace sdl
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
||||
#define XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
@@ -58,6 +59,14 @@ class SDLAudioDriver : public AudioDriver {
|
||||
std::queue<float*> frames_queued_ = {};
|
||||
std::stack<float*> frames_unused_ = {};
|
||||
std::mutex frames_mutex_ = {};
|
||||
|
||||
std::atomic<uint64_t> frames_submitted_{0};
|
||||
std::atomic<uint64_t> frames_played_{0};
|
||||
std::atomic<uint64_t> underrun_count_{0};
|
||||
std::atomic<uint64_t> low_queue_count_{0};
|
||||
std::atomic<uint64_t> last_reported_underrun_count_{0};
|
||||
std::atomic<uint64_t> last_reported_low_queue_count_{0};
|
||||
std::atomic<size_t> max_queue_depth_{0};
|
||||
};
|
||||
|
||||
} // namespace sdl
|
||||
|
||||
@@ -9,13 +9,46 @@
|
||||
|
||||
#include "xenia/apu/sdl/sdl_audio_system.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "xenia/apu/apu_flags.h"
|
||||
#include "xenia/apu/sdl/sdl_audio_driver.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
namespace xe {
|
||||
namespace apu {
|
||||
namespace sdl {
|
||||
|
||||
namespace {
|
||||
|
||||
#if XE_PLATFORM_IOS
|
||||
// Fallback used when SDL/CoreAudio device creation fails on iOS.
|
||||
// Keeps guest audio callback flow alive without real audio output.
|
||||
class SilentAudioDriver final : public AudioDriver {
|
||||
public:
|
||||
explicit SilentAudioDriver(xe::threading::Semaphore* semaphore)
|
||||
: semaphore_(semaphore) {}
|
||||
|
||||
bool Initialize() override { return true; }
|
||||
void Shutdown() override {}
|
||||
void SubmitFrame(float* samples) override {
|
||||
(void)samples;
|
||||
if (semaphore_) {
|
||||
semaphore_->Release(1, nullptr);
|
||||
}
|
||||
}
|
||||
void Pause() override {}
|
||||
void Resume() override {}
|
||||
void SetVolume(float volume) override { (void)volume; }
|
||||
|
||||
private:
|
||||
xe::threading::Semaphore* semaphore_ = nullptr;
|
||||
};
|
||||
#endif // XE_PLATFORM_IOS
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<AudioSystem> SDLAudioSystem::Create(cpu::Processor* processor) {
|
||||
return std::make_unique<SDLAudioSystem>(processor);
|
||||
}
|
||||
@@ -31,10 +64,32 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
||||
xe::threading::Semaphore* semaphore,
|
||||
AudioDriver** out_driver) {
|
||||
assert_not_null(out_driver);
|
||||
#if XE_PLATFORM_IOS
|
||||
// iOS/SDL audio path is single-output in practice here. Additional guest
|
||||
// clients should not attempt to open another device because SDL reports
|
||||
// "Audio device already open" and we fall back to silence anyway.
|
||||
if (index > 0) {
|
||||
static std::atomic<bool> logged_secondary_fallback{false};
|
||||
if (!logged_secondary_fallback.exchange(true, std::memory_order_relaxed)) {
|
||||
XELOGW(
|
||||
"SDLAudioSystem: iOS secondary audio clients use silent fallback; "
|
||||
"keeping primary SDL device on client 0");
|
||||
}
|
||||
*out_driver = new SilentAudioDriver(semaphore);
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto driver = std::make_unique<SDLAudioDriver>(semaphore);
|
||||
if (!driver->Initialize()) {
|
||||
driver->Shutdown();
|
||||
#if XE_PLATFORM_IOS
|
||||
XELOGW("SDLAudioSystem: SDL audio init failed, using silent fallback");
|
||||
*out_driver = new SilentAudioDriver(semaphore);
|
||||
return X_STATUS_SUCCESS;
|
||||
#else
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
#endif
|
||||
}
|
||||
|
||||
*out_driver = driver.release();
|
||||
@@ -44,12 +99,29 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
||||
AudioDriver* SDLAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore,
|
||||
uint32_t frequency, uint32_t channels,
|
||||
bool need_format_conversion) {
|
||||
#if XE_PLATFORM_IOS
|
||||
static std::atomic<bool> logged_independent_fallback{false};
|
||||
if (!logged_independent_fallback.exchange(true, std::memory_order_relaxed)) {
|
||||
XELOGW(
|
||||
"SDLAudioSystem: iOS independent audio drivers use silent fallback; "
|
||||
"secondary SDL/CoreAudio devices are not opened");
|
||||
}
|
||||
return new SilentAudioDriver(semaphore);
|
||||
#else
|
||||
return new SDLAudioDriver(semaphore, frequency, channels,
|
||||
need_format_conversion);
|
||||
#endif // XE_PLATFORM_IOS
|
||||
}
|
||||
|
||||
void SDLAudioSystem::DestroyDriver(AudioDriver* driver) {
|
||||
assert_not_null(driver);
|
||||
#if XE_PLATFORM_IOS
|
||||
if (auto* silent_driver = dynamic_cast<SilentAudioDriver*>(driver)) {
|
||||
silent_driver->Shutdown();
|
||||
delete silent_driver;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
auto sdldriver = dynamic_cast<SDLAudioDriver*>(driver);
|
||||
assert_not_null(sdldriver);
|
||||
sdldriver->Shutdown();
|
||||
|
||||
@@ -10,6 +10,12 @@ if(_base_exclude AND _base_srcs)
|
||||
list(REMOVE_ITEM _base_srcs ${_base_exclude})
|
||||
set_target_properties(xenia-base PROPERTIES SOURCES "${_base_srcs}")
|
||||
endif()
|
||||
if(XE_PLATFORM_IOS)
|
||||
# This file is named for the original macOS implementation, but the
|
||||
# Objective-C autorelease pool runtime is Apple-wide.
|
||||
target_sources(xenia-base PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/autorelease_pool_mac.cc)
|
||||
endif()
|
||||
target_link_libraries(xenia-base PUBLIC fmt)
|
||||
target_link_libraries(xenia-base PRIVATE zlib-ng boost_context)
|
||||
xe_target_defaults(xenia-base)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "xenia/base/autorelease_pool_mac.h"
|
||||
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
|
||||
#include <pthread.h>
|
||||
#include <chrono>
|
||||
@@ -174,4 +174,4 @@ ScopedAutoreleasePool::~ScopedAutoreleasePool() {
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -94,13 +94,13 @@ class ScopedAutoreleasePool {
|
||||
#define XE_SCOPED_AUTORELEASE_POOL(name) \
|
||||
xe::ScopedAutoreleasePool _autorelease_pool_##__LINE__(name)
|
||||
|
||||
#else // !XE_PLATFORM_MAC
|
||||
#else // !XE_PLATFORM_APPLE
|
||||
|
||||
#define XE_AUTORELEASE_POOL_PUSH(location) nullptr
|
||||
#define XE_AUTORELEASE_POOL_POP(pool, location) (void)0
|
||||
#define XE_AUTORELEASE_POOL_CHECK_LEAKS() (void)0
|
||||
#define XE_SCOPED_AUTORELEASE_POOL(name) (void)0
|
||||
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
|
||||
#endif // XENIA_BASE_AUTORELEASE_POOL_MAC_H_
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2026 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <libkern/OSCacheControl.h>
|
||||
|
||||
extern "C" void __clear_cache(void* start, void* end) {
|
||||
auto* start_bytes = static_cast<uint8_t*>(start);
|
||||
auto* end_bytes = static_cast<uint8_t*>(end);
|
||||
if (end_bytes <= start_bytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
sys_icache_invalidate(start, static_cast<size_t>(end_bytes - start_bytes));
|
||||
}
|
||||
@@ -39,7 +39,7 @@ std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
|
||||
|
||||
static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
void* signal_context) {
|
||||
#if XE_PLATFORM_MAC && XE_ARCH_ARM64
|
||||
#if XE_PLATFORM_APPLE && XE_ARCH_ARM64
|
||||
// The Darwin kernel may pass an unaligned ucontext_t pointer to signal
|
||||
// handlers; copy into an aligned local before reading. mcontext_t is a
|
||||
// pointer on Mac, so writes still reach kernel storage via the pointer.
|
||||
@@ -54,7 +54,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
HostThreadContext thread_context;
|
||||
|
||||
#if XE_ARCH_AMD64
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
// Darwin: mcontext is a pointer; integer state in __ss, FP/XMM in __fs.
|
||||
// __fpu_xmm0..__fpu_xmm15 are laid out contiguously in
|
||||
// __darwin_x86_float_state64.
|
||||
@@ -101,9 +101,9 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]);
|
||||
std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
|
||||
sizeof(thread_context.xmm_registers));
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
#elif XE_ARCH_ARM64
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
// Darwin: mcontext is a pointer, registers in __ss and __ns.
|
||||
for (int i = 0; i < 29; ++i) {
|
||||
thread_context.x[i] = mcontext->__ss.__x[i];
|
||||
@@ -150,7 +150,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
std::memcpy(thread_context.v, mcontext_fpsimd->vregs,
|
||||
sizeof(thread_context.v));
|
||||
}
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
#endif // XE_ARCH
|
||||
|
||||
Exception ex;
|
||||
@@ -164,7 +164,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
#if XE_ARCH_AMD64
|
||||
// x86_pf_error_code::X86_PF_WRITE
|
||||
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
access_violation_operation =
|
||||
(uint64_t(mcontext->__es.__err) & kX86PageFaultErrorCodeWrite)
|
||||
? Exception::AccessViolationOperation::kWrite
|
||||
@@ -176,7 +176,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
: Exception::AccessViolationOperation::kRead;
|
||||
#endif
|
||||
#elif XE_ARCH_ARM64
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
{
|
||||
// On Darwin, determine access direction from the faulting instruction.
|
||||
uint64_t fault_pc = mcontext->__ss.__pc;
|
||||
@@ -242,7 +242,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
Exception::AccessViolationOperation::kUnknown;
|
||||
}
|
||||
}
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
#else
|
||||
access_violation_operation =
|
||||
Exception::AccessViolationOperation::kUnknown;
|
||||
@@ -260,7 +260,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
// Exception handled.
|
||||
#if XE_ARCH_AMD64
|
||||
uint32_t modified_register_index;
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
mcontext->__ss.__rip = thread_context.rip;
|
||||
mcontext->__ss.__rflags = thread_context.eflags;
|
||||
// Pointer-to-member map; order must match X64Register.
|
||||
@@ -327,10 +327,10 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
&thread_context.xmm_registers[modified_register_index],
|
||||
sizeof(vec128_t));
|
||||
}
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
#elif XE_ARCH_ARM64
|
||||
uint32_t modified_register_index;
|
||||
#if XE_PLATFORM_MAC
|
||||
#if XE_PLATFORM_APPLE
|
||||
uint32_t modified_x_registers_remaining = ex.modified_x_registers();
|
||||
while (xe::bit_scan_forward(modified_x_registers_remaining,
|
||||
&modified_register_index)) {
|
||||
@@ -386,7 +386,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||
thread_context.x[modified_register_index];
|
||||
}
|
||||
}
|
||||
#endif // XE_PLATFORM_MAC
|
||||
#endif // XE_PLATFORM_APPLE
|
||||
#endif // XE_ARCH
|
||||
return;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user