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-vs/
|
||||||
/build-arm64/
|
/build-arm64/
|
||||||
/build-x64/
|
/build-x64/
|
||||||
|
/build-ios/
|
||||||
|
/build-ios-xcode/
|
||||||
|
|
||||||
# CMake user-specific overrides + compile commands export
|
# CMake user-specific overrides + compile commands export
|
||||||
CMakeUserPresets.json
|
CMakeUserPresets.json
|
||||||
@@ -105,6 +107,7 @@ CMakeUserPresets.json
|
|||||||
/third_party/binutils/bin/
|
/third_party/binutils/bin/
|
||||||
/third_party/binutils/powerpc-none-elf/
|
/third_party/binutils/powerpc-none-elf/
|
||||||
/third_party/binutils/share/
|
/third_party/binutils/share/
|
||||||
|
/third_party/MoltenVK/
|
||||||
/third_party/vasm/
|
/third_party/vasm/
|
||||||
/tools/shader-playground/*.dll
|
/tools/shader-playground/*.dll
|
||||||
/profile_print_times.py
|
/profile_print_times.py
|
||||||
|
|||||||
+41
-16
@@ -1,15 +1,30 @@
|
|||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.20)
|
||||||
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
|
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
|
||||||
|
|
||||||
# Must be set before project() so the toolchain bakes -mmacosx-version-min
|
# Keep Apple platform selection explicit. CMake sets APPLE for both macOS and
|
||||||
# into every compile/link; setting it later only updates the variable.
|
# iOS, but the two targets need different bundles, frameworks and dependencies.
|
||||||
if(APPLE)
|
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")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS version")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Suppress macOS ranlib warnings for empty object files. wxWidgets's vendored
|
# Suppress macOS ranlib warnings for empty object files. wxWidgets's vendored
|
||||||
# libpng compiles Intel/MIPS/PowerPC sources that are empty on ARM64.
|
# 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_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_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
|
||||||
set(CMAKE_OBJC_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.
|
# Detect target architecture.
|
||||||
# - VS generator: CMAKE_GENERATOR_PLATFORM (from -A flag) takes priority.
|
# - VS generator: CMAKE_GENERATOR_PLATFORM (from -A flag) takes priority.
|
||||||
# - macOS: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds
|
# - Apple: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds would
|
||||||
# would need both backends compiled, which we don't support).
|
# need both backends compiled, which we don't support).
|
||||||
# - Ninja/Makefiles: CMAKE_SYSTEM_PROCESSOR (set via -DCMAKE_SYSTEM_NAME
|
# - Ninja/Makefiles: CMAKE_SYSTEM_PROCESSOR (set via -DCMAKE_SYSTEM_NAME
|
||||||
# + -DCMAKE_SYSTEM_PROCESSOR for cross-compile, or auto-detected natively).
|
# + -DCMAKE_SYSTEM_PROCESSOR for cross-compile, or auto-detected natively).
|
||||||
if(CMAKE_GENERATOR_PLATFORM)
|
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_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_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_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)
|
if(XENIA_ENABLE_PROFILER)
|
||||||
# UI is Debug-only — shutdown dump spams the log otherwise.
|
# 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)
|
if(WIN32)
|
||||||
set(XE_PLATFORM_NAME "Windows")
|
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")
|
set(XE_PLATFORM_NAME "macOS")
|
||||||
else()
|
else()
|
||||||
set(XE_PLATFORM_NAME "Linux")
|
set(XE_PLATFORM_NAME "Linux")
|
||||||
@@ -327,7 +345,7 @@ else()
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
# macOS-specific settings
|
# Apple-specific settings.
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wno-unknown-warning-option
|
-Wno-unknown-warning-option
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-shorten-64-to-32>
|
$<$<COMPILE_LANGUAGE:CXX>:-Wno-shorten-64-to-32>
|
||||||
@@ -336,20 +354,27 @@ else()
|
|||||||
-fno-common
|
-fno-common
|
||||||
)
|
)
|
||||||
add_link_options(-Wl,-no_warn_duplicate_libraries)
|
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
|
# macOS 64-bit binaries default to a 4GB __PAGEZERO, which makes every
|
||||||
# address below 4GB unmapped. The x64 backend places its constant
|
# address below 4GB unmapped. The x64 backend places its constant
|
||||||
# table (PlaceConstData) sub-2GB and its guest trampolines sub-4GB,
|
# table (PlaceConstData) sub-2GB and its guest trampolines sub-4GB,
|
||||||
# so we shrink __PAGEZERO to one page to free up that range.
|
# so we shrink __PAGEZERO to one page to free up that range.
|
||||||
add_link_options(-Wl,-pagezero_size,0x1000)
|
add_link_options(-Wl,-pagezero_size,0x1000)
|
||||||
endif()
|
endif()
|
||||||
link_libraries(
|
if(XE_PLATFORM_MACOS)
|
||||||
"-framework CoreFoundation"
|
link_libraries(
|
||||||
"-framework Foundation"
|
"-framework CoreFoundation"
|
||||||
"-framework IOKit"
|
"-framework Foundation"
|
||||||
dl
|
"-framework IOKit"
|
||||||
pthread
|
dl
|
||||||
)
|
pthread
|
||||||
|
)
|
||||||
|
elseif(XE_PLATFORM_IOS)
|
||||||
|
link_libraries(
|
||||||
|
"-framework CoreFoundation"
|
||||||
|
"-framework Foundation"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0)
|
pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0)
|
||||||
|
|||||||
@@ -1,20 +1,100 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/xenia-canary/xenia-canary/tree/canary_experimental/assets/icon">
|
<img height="256px" src="assets/apple/xenios-readme-icon.png" alt="XeniOS app icon" />
|
||||||
<img height="256px" src="https://raw.githubusercontent.com/xenia-canary/xenia/master/assets/icon/256.png" />
|
|
||||||
</a>
|
|
||||||
</p>
|
</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
|
XeniOS is an experimental Apple-focused fork of Xenia, currently based on
|
||||||
on faster iteration, higher default game compatibility, usability and platform support.
|
[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
|
- iOS
|
||||||
--------
|
- macOS (Apple Silicon)
|
||||||
[Latest](https://github.com/has207/xenia-edge/releases/latest) ◦ [All](https://github.com/has207/xenia-edge/releases)
|
- 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)
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
set(XE_PLATFORM_SUFFIXES
|
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])
|
# 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.h"
|
||||||
"${base_path}/*_win.cc"
|
"${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
|
file(${glob_mode} _plat_sources
|
||||||
"${base_path}/*_posix.h"
|
"${base_path}/*_posix.h"
|
||||||
"${base_path}/*_posix.cc"
|
"${base_path}/*_posix.cc"
|
||||||
@@ -106,6 +113,8 @@ function(xe_target_defaults target)
|
|||||||
)
|
)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(${target} PRIVATE /WX)
|
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")
|
elseif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
target_compile_options(${target} PRIVATE -Werror)
|
target_compile_options(${target} PRIVATE -Werror)
|
||||||
endif()
|
endif()
|
||||||
@@ -327,6 +336,13 @@ function(xe_shader_rules_metal target shader_dir)
|
|||||||
set(_generated_root "${PROJECT_BINARY_DIR}/generated")
|
set(_generated_root "${PROJECT_BINARY_DIR}/generated")
|
||||||
set(_bytecode_dir "${_generated_root}/${_rel_dir}/bytecode/metal")
|
set(_bytecode_dir "${_generated_root}/${_rel_dir}/bytecode/metal")
|
||||||
set(_valid_stages vs ps cs)
|
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)
|
set(_outputs)
|
||||||
file(MAKE_DIRECTORY "${_bytecode_dir}")
|
file(MAKE_DIRECTORY "${_bytecode_dir}")
|
||||||
foreach(src ${_sources})
|
foreach(src ${_sources})
|
||||||
@@ -350,7 +366,7 @@ function(xe_shader_rules_metal target shader_dir)
|
|||||||
list(APPEND _outputs "${_out}")
|
list(APPEND _outputs "${_out}")
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${_out}"
|
OUTPUT "${_out}"
|
||||||
COMMAND $<TARGET_FILE:xenia-shader-cc> --msl --depfile "${_dep}"
|
COMMAND $<TARGET_FILE:xenia-shader-cc> ${_metal_args} --depfile "${_dep}"
|
||||||
"${src}" "${_out}"
|
"${src}" "${_out}"
|
||||||
DEPENDS "${src}" xenia-shader-cc
|
DEPENDS "${src}" xenia-shader-cc
|
||||||
DEPFILE "${_dep}"
|
DEPFILE "${_dep}"
|
||||||
|
|||||||
@@ -28,19 +28,25 @@ if(APPLE)
|
|||||||
endif()
|
endif()
|
||||||
add_subdirectory(helper/sdl)
|
add_subdirectory(helper/sdl)
|
||||||
add_subdirectory(hid)
|
add_subdirectory(hid)
|
||||||
|
add_subdirectory(hid/touch)
|
||||||
add_subdirectory(hid/nop)
|
add_subdirectory(hid/nop)
|
||||||
add_subdirectory(hid/sdl)
|
add_subdirectory(hid/sdl)
|
||||||
add_subdirectory(hid/portal)
|
add_subdirectory(hid/portal)
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
add_subdirectory(patcher)
|
add_subdirectory(patcher)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
|
if(XE_PLATFORM_IOS)
|
||||||
|
add_subdirectory(ui/ios)
|
||||||
|
endif()
|
||||||
add_subdirectory(ui/vulkan)
|
add_subdirectory(ui/vulkan)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_subdirectory(ui/metal)
|
add_subdirectory(ui/metal)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(vfs)
|
add_subdirectory(vfs)
|
||||||
add_subdirectory(debug/ui)
|
if(NOT XE_PLATFORM_IOS)
|
||||||
add_subdirectory(app/discord)
|
add_subdirectory(debug/ui)
|
||||||
|
add_subdirectory(app/discord)
|
||||||
|
endif()
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
|
|
||||||
if(WIN32)
|
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)
|
if(WIN32)
|
||||||
add_executable(xenia-app WIN32)
|
add_executable(xenia-app WIN32)
|
||||||
elseif(APPLE)
|
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 "third_party/stb/stb_image.h"
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/vfs/iso_metadata.h"
|
#include "xenia/vfs/iso_metadata.h"
|
||||||
#include "xenia/vfs/stfs_metadata.h"
|
#include "xenia/vfs/stfs_metadata.h"
|
||||||
#include "xenia/vfs/xex_metadata.h"
|
#include "xenia/vfs/xex_metadata.h"
|
||||||
@@ -173,6 +174,10 @@ size_t DirectoryScanner::CountResultsWithTitleId(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryScanner::Run(std::filesystem::path root) {
|
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));
|
XELOGI("DirectoryScanner: scan start, root='{}'", xe::path_to_utf8(root));
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (!std::filesystem::is_directory(root, ec) || ec) {
|
if (!std::filesystem::is_directory(root, ec) || ec) {
|
||||||
@@ -219,6 +224,10 @@ void DirectoryScanner::Run(std::filesystem::path root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DirectoryScanner::WorkerLoop() {
|
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) {
|
while (true) {
|
||||||
std::filesystem::path dir;
|
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 "xenia/apu/audio_system.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "xenia/apu/apu_flags.h"
|
#include "xenia/apu/apu_flags.h"
|
||||||
#include "xenia/apu/audio_driver.h"
|
#include "xenia/apu/audio_driver.h"
|
||||||
#include "xenia/apu/xma_decoder.h"
|
#include "xenia/apu/xma_decoder.h"
|
||||||
@@ -35,7 +37,21 @@
|
|||||||
// and let the normal AudioSystem handling take it, to prevent duplicate
|
// and let the normal AudioSystem handling take it, to prevent duplicate
|
||||||
// implementations. They can be found in xboxkrnl_audio_xma.cc
|
// 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 "
|
"Allows changing max buffered audio frames to reduce audio "
|
||||||
"delay. Lowering this value might cause performance issues. "
|
"delay. Lowering this value might cause performance issues. "
|
||||||
"Value range: [4-64]",
|
"Value range: [4-64]",
|
||||||
@@ -97,11 +113,26 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioSystem::WorkerThreadMain() {
|
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 driver and ringbuffer.
|
||||||
Initialize();
|
Initialize();
|
||||||
|
|
||||||
// Main run loop.
|
// Main run loop.
|
||||||
while (worker_running_) {
|
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
|
// These handles signify the number of submitted samples. Once we reach
|
||||||
// 64 samples, we wait until our audio backend releases a semaphore
|
// 64 samples, we wait until our audio backend releases a semaphore
|
||||||
// (signaling a sample has finished playing)
|
// (signaling a sample has finished playing)
|
||||||
@@ -112,10 +143,14 @@ void AudioSystem::WorkerThreadMain() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!worker_running_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.first == threading::WaitResult::kSuccess &&
|
if (result.first == threading::WaitResult::kSuccess &&
|
||||||
result.second == kMaximumClientCount) {
|
result.second == kMaximumClientCount) {
|
||||||
// Shutdown event signaled.
|
// Shutdown event signaled.
|
||||||
if (paused_) {
|
if (paused_.load(std::memory_order_acquire)) {
|
||||||
pause_fence_.Signal();
|
pause_fence_.Signal();
|
||||||
threading::Wait(resume_event_.get(), false);
|
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) {
|
if (client_callback) {
|
||||||
SCOPE_profile_cpu_i("apu", "xe::apu::AudioSystem->client_callback");
|
SCOPE_profile_cpu_i("apu", "xe::apu::AudioSystem->client_callback");
|
||||||
uint64_t args[] = {client_callback_arg};
|
uint64_t args[] = {client_callback_arg};
|
||||||
@@ -148,6 +189,12 @@ void AudioSystem::WorkerThreadMain() {
|
|||||||
args, xe::countof(args));
|
args, xe::countof(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if XE_PLATFORM_IOS
|
||||||
|
if (IsTitleStopRequested(processor_)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // XE_PLATFORM_IOS
|
||||||
|
|
||||||
pumped = true;
|
pumped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +228,12 @@ void AudioSystem::Initialize() {}
|
|||||||
void AudioSystem::Shutdown() {
|
void AudioSystem::Shutdown() {
|
||||||
worker_running_ = false;
|
worker_running_ = false;
|
||||||
shutdown_event_->Set();
|
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_) {
|
if (worker_thread_) {
|
||||||
worker_thread_->Wait(0, 0, 0, nullptr);
|
worker_thread_->Wait(0, 0, 0, nullptr);
|
||||||
worker_thread_.reset();
|
worker_thread_.reset();
|
||||||
@@ -411,10 +464,9 @@ bool AudioSystem::Restore(ByteStream* stream) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioSystem::Pause() {
|
void AudioSystem::Pause() {
|
||||||
if (paused_) {
|
if (paused_.exchange(true, std::memory_order_acq_rel)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paused_ = true;
|
|
||||||
|
|
||||||
// Kind of a hack, but it works.
|
// Kind of a hack, but it works.
|
||||||
shutdown_event_->Set();
|
shutdown_event_->Set();
|
||||||
@@ -424,10 +476,9 @@ void AudioSystem::Pause() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioSystem::Resume() {
|
void AudioSystem::Resume() {
|
||||||
if (!paused_) {
|
if (!paused_.exchange(false, std::memory_order_acq_rel)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
paused_ = false;
|
|
||||||
|
|
||||||
resume_event_->Set();
|
resume_event_->Set();
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class AudioSystem {
|
|||||||
bool Save(ByteStream* stream);
|
bool Save(ByteStream* stream);
|
||||||
bool Restore(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 Pause();
|
||||||
void Resume();
|
void Resume();
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ class AudioSystem {
|
|||||||
std::unique_ptr<xe::threading::Event> shutdown_event_;
|
std::unique_ptr<xe::threading::Event> shutdown_event_;
|
||||||
xe::threading::WaitHandle* wait_handles_[kMaximumClientCount + 1];
|
xe::threading::WaitHandle* wait_handles_[kMaximumClientCount + 1];
|
||||||
|
|
||||||
bool paused_ = false;
|
std::atomic<bool> paused_ = false;
|
||||||
threading::Fence pause_fence_;
|
threading::Fence pause_fence_;
|
||||||
std::unique_ptr<threading::Event> resume_event_;
|
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
|
#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 macOS
|
// load_be_u32 unavailable on clang-cl, clang on Linux, or clang on Apple
|
||||||
XE_NOINLINE
|
XE_NOINLINE
|
||||||
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
|
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
|
||||||
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
||||||
|
|||||||
@@ -22,6 +22,20 @@ namespace xe {
|
|||||||
namespace apu {
|
namespace apu {
|
||||||
namespace sdl {
|
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,
|
SDLAudioDriver::SDLAudioDriver(xe::threading::Semaphore* semaphore,
|
||||||
uint32_t frequency, uint32_t channels,
|
uint32_t frequency, uint32_t channels,
|
||||||
bool need_format_conversion)
|
bool need_format_conversion)
|
||||||
@@ -115,7 +129,57 @@ void SDLAudioDriver::SubmitFrame(float* frame) {
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> guard(frames_mutex_);
|
std::unique_lock<std::mutex> guard(frames_mutex_);
|
||||||
frames_queued_.push(output_frame);
|
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() {
|
void SDLAudioDriver::Pause() {
|
||||||
@@ -169,6 +233,10 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
|
||||||
if (driver->frames_queued_.empty()) {
|
if (driver->frames_queued_.empty()) {
|
||||||
|
driver->underrun_count_.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
if (driver->semaphore_) {
|
||||||
|
driver->semaphore_->Release(1, nullptr);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buffer = driver->frames_queued_.front();
|
buffer = driver->frames_queued_.front();
|
||||||
@@ -213,8 +281,11 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
|
|||||||
driver->frames_unused_.push(buffer);
|
driver->frames_unused_.push(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret = driver->semaphore_->Release(1, nullptr);
|
driver->frames_played_.fetch_add(1, std::memory_order_relaxed);
|
||||||
assert_true(ret);
|
if (driver->semaphore_) {
|
||||||
|
auto ret = driver->semaphore_->Release(1, nullptr);
|
||||||
|
assert_true(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace sdl
|
} // namespace sdl
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#ifndef XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
#ifndef XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
||||||
#define XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
#define XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
@@ -58,6 +59,14 @@ class SDLAudioDriver : public AudioDriver {
|
|||||||
std::queue<float*> frames_queued_ = {};
|
std::queue<float*> frames_queued_ = {};
|
||||||
std::stack<float*> frames_unused_ = {};
|
std::stack<float*> frames_unused_ = {};
|
||||||
std::mutex frames_mutex_ = {};
|
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
|
} // namespace sdl
|
||||||
|
|||||||
@@ -9,13 +9,46 @@
|
|||||||
|
|
||||||
#include "xenia/apu/sdl/sdl_audio_system.h"
|
#include "xenia/apu/sdl/sdl_audio_system.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "xenia/apu/apu_flags.h"
|
#include "xenia/apu/apu_flags.h"
|
||||||
#include "xenia/apu/sdl/sdl_audio_driver.h"
|
#include "xenia/apu/sdl/sdl_audio_driver.h"
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/threading.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace apu {
|
namespace apu {
|
||||||
namespace sdl {
|
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) {
|
std::unique_ptr<AudioSystem> SDLAudioSystem::Create(cpu::Processor* processor) {
|
||||||
return std::make_unique<SDLAudioSystem>(processor);
|
return std::make_unique<SDLAudioSystem>(processor);
|
||||||
}
|
}
|
||||||
@@ -31,10 +64,32 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
|||||||
xe::threading::Semaphore* semaphore,
|
xe::threading::Semaphore* semaphore,
|
||||||
AudioDriver** out_driver) {
|
AudioDriver** out_driver) {
|
||||||
assert_not_null(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);
|
auto driver = std::make_unique<SDLAudioDriver>(semaphore);
|
||||||
if (!driver->Initialize()) {
|
if (!driver->Initialize()) {
|
||||||
driver->Shutdown();
|
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;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_driver = driver.release();
|
*out_driver = driver.release();
|
||||||
@@ -44,12 +99,29 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
|
|||||||
AudioDriver* SDLAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore,
|
AudioDriver* SDLAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore,
|
||||||
uint32_t frequency, uint32_t channels,
|
uint32_t frequency, uint32_t channels,
|
||||||
bool need_format_conversion) {
|
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,
|
return new SDLAudioDriver(semaphore, frequency, channels,
|
||||||
need_format_conversion);
|
need_format_conversion);
|
||||||
|
#endif // XE_PLATFORM_IOS
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLAudioSystem::DestroyDriver(AudioDriver* driver) {
|
void SDLAudioSystem::DestroyDriver(AudioDriver* driver) {
|
||||||
assert_not_null(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);
|
auto sdldriver = dynamic_cast<SDLAudioDriver*>(driver);
|
||||||
assert_not_null(sdldriver);
|
assert_not_null(sdldriver);
|
||||||
sdldriver->Shutdown();
|
sdldriver->Shutdown();
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ if(_base_exclude AND _base_srcs)
|
|||||||
list(REMOVE_ITEM _base_srcs ${_base_exclude})
|
list(REMOVE_ITEM _base_srcs ${_base_exclude})
|
||||||
set_target_properties(xenia-base PROPERTIES SOURCES "${_base_srcs}")
|
set_target_properties(xenia-base PROPERTIES SOURCES "${_base_srcs}")
|
||||||
endif()
|
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 PUBLIC fmt)
|
||||||
target_link_libraries(xenia-base PRIVATE zlib-ng boost_context)
|
target_link_libraries(xenia-base PRIVATE zlib-ng boost_context)
|
||||||
xe_target_defaults(xenia-base)
|
xe_target_defaults(xenia-base)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include "xenia/base/autorelease_pool_mac.h"
|
#include "xenia/base/autorelease_pool_mac.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -174,4 +174,4 @@ ScopedAutoreleasePool::~ScopedAutoreleasePool() {
|
|||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
||||||
@@ -94,13 +94,13 @@ class ScopedAutoreleasePool {
|
|||||||
#define XE_SCOPED_AUTORELEASE_POOL(name) \
|
#define XE_SCOPED_AUTORELEASE_POOL(name) \
|
||||||
xe::ScopedAutoreleasePool _autorelease_pool_##__LINE__(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_PUSH(location) nullptr
|
||||||
#define XE_AUTORELEASE_POOL_POP(pool, location) (void)0
|
#define XE_AUTORELEASE_POOL_POP(pool, location) (void)0
|
||||||
#define XE_AUTORELEASE_POOL_CHECK_LEAKS() (void)0
|
#define XE_AUTORELEASE_POOL_CHECK_LEAKS() (void)0
|
||||||
#define XE_SCOPED_AUTORELEASE_POOL(name) (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_
|
#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,
|
static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
||||||
void* signal_context) {
|
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
|
// The Darwin kernel may pass an unaligned ucontext_t pointer to signal
|
||||||
// handlers; copy into an aligned local before reading. mcontext_t is a
|
// handlers; copy into an aligned local before reading. mcontext_t is a
|
||||||
// pointer on Mac, so writes still reach kernel storage via the pointer.
|
// 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;
|
HostThreadContext thread_context;
|
||||||
|
|
||||||
#if XE_ARCH_AMD64
|
#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.
|
// Darwin: mcontext is a pointer; integer state in __ss, FP/XMM in __fs.
|
||||||
// __fpu_xmm0..__fpu_xmm15 are laid out contiguously in
|
// __fpu_xmm0..__fpu_xmm15 are laid out contiguously in
|
||||||
// __darwin_x86_float_state64.
|
// __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]);
|
thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]);
|
||||||
std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
|
std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
|
||||||
sizeof(thread_context.xmm_registers));
|
sizeof(thread_context.xmm_registers));
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
#elif XE_ARCH_ARM64
|
#elif XE_ARCH_ARM64
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
// Darwin: mcontext is a pointer, registers in __ss and __ns.
|
// Darwin: mcontext is a pointer, registers in __ss and __ns.
|
||||||
for (int i = 0; i < 29; ++i) {
|
for (int i = 0; i < 29; ++i) {
|
||||||
thread_context.x[i] = mcontext->__ss.__x[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,
|
std::memcpy(thread_context.v, mcontext_fpsimd->vregs,
|
||||||
sizeof(thread_context.v));
|
sizeof(thread_context.v));
|
||||||
}
|
}
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
#endif // XE_ARCH
|
#endif // XE_ARCH
|
||||||
|
|
||||||
Exception ex;
|
Exception ex;
|
||||||
@@ -164,7 +164,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||||||
#if XE_ARCH_AMD64
|
#if XE_ARCH_AMD64
|
||||||
// x86_pf_error_code::X86_PF_WRITE
|
// x86_pf_error_code::X86_PF_WRITE
|
||||||
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
|
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
access_violation_operation =
|
access_violation_operation =
|
||||||
(uint64_t(mcontext->__es.__err) & kX86PageFaultErrorCodeWrite)
|
(uint64_t(mcontext->__es.__err) & kX86PageFaultErrorCodeWrite)
|
||||||
? Exception::AccessViolationOperation::kWrite
|
? Exception::AccessViolationOperation::kWrite
|
||||||
@@ -176,7 +176,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||||||
: Exception::AccessViolationOperation::kRead;
|
: Exception::AccessViolationOperation::kRead;
|
||||||
#endif
|
#endif
|
||||||
#elif XE_ARCH_ARM64
|
#elif XE_ARCH_ARM64
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
{
|
{
|
||||||
// On Darwin, determine access direction from the faulting instruction.
|
// On Darwin, determine access direction from the faulting instruction.
|
||||||
uint64_t fault_pc = mcontext->__ss.__pc;
|
uint64_t fault_pc = mcontext->__ss.__pc;
|
||||||
@@ -242,7 +242,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||||||
Exception::AccessViolationOperation::kUnknown;
|
Exception::AccessViolationOperation::kUnknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
#else
|
#else
|
||||||
access_violation_operation =
|
access_violation_operation =
|
||||||
Exception::AccessViolationOperation::kUnknown;
|
Exception::AccessViolationOperation::kUnknown;
|
||||||
@@ -260,7 +260,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||||||
// Exception handled.
|
// Exception handled.
|
||||||
#if XE_ARCH_AMD64
|
#if XE_ARCH_AMD64
|
||||||
uint32_t modified_register_index;
|
uint32_t modified_register_index;
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
mcontext->__ss.__rip = thread_context.rip;
|
mcontext->__ss.__rip = thread_context.rip;
|
||||||
mcontext->__ss.__rflags = thread_context.eflags;
|
mcontext->__ss.__rflags = thread_context.eflags;
|
||||||
// Pointer-to-member map; order must match X64Register.
|
// 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],
|
&thread_context.xmm_registers[modified_register_index],
|
||||||
sizeof(vec128_t));
|
sizeof(vec128_t));
|
||||||
}
|
}
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
#elif XE_ARCH_ARM64
|
#elif XE_ARCH_ARM64
|
||||||
uint32_t modified_register_index;
|
uint32_t modified_register_index;
|
||||||
#if XE_PLATFORM_MAC
|
#if XE_PLATFORM_APPLE
|
||||||
uint32_t modified_x_registers_remaining = ex.modified_x_registers();
|
uint32_t modified_x_registers_remaining = ex.modified_x_registers();
|
||||||
while (xe::bit_scan_forward(modified_x_registers_remaining,
|
while (xe::bit_scan_forward(modified_x_registers_remaining,
|
||||||
&modified_register_index)) {
|
&modified_register_index)) {
|
||||||
@@ -386,7 +386,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
|
|||||||
thread_context.x[modified_register_index];
|
thread_context.x[modified_register_index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // XE_PLATFORM_MAC
|
#endif // XE_PLATFORM_APPLE
|
||||||
#endif // XE_ARCH
|
#endif // XE_ARCH
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user