mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
iOS: gate macOS-only APIs behind TARGET_OS_IPHONE and add frontend link stubs
Gate all macOS-only system APIs behind TARGET_OS_IPHONE checks across the shared core: ApplicationServices, IOKit, mouse APIs, AppKit, MetalFX, CDVD Darwin sources, USB, discord-rpc, cubeb CoreAudio HAL, and BSD networking headers in DEV9. Port TARGET_OS_IPHONE guards for DEV9 AdapterUtils with iOS fallbacks for sockaddr_dl, rt_msghdr, and sysctl. Add ARMSX2_ROOT to the include path for the frontend's common include style. Drop the global _M_ARM64 define that triggered fast_float MSVC intrin.h inclusion since __aarch64__ covers the core paths. Gate MTLFeatureSet_macOS_GPUFamily1_v1 behind !TARGET_OS_IPHONE. Fix missing unistd.h and gate pthread_jit_write_protect_np which is unavailable on iOS. Add stubs for DarwinMisc JIT diagnostics, Achievements stats and info APIs, Discord_Register, Host capture callbacks, and the host hotkeys map so the frontend links cleanly. Merge upstream master.
This commit is contained in:
@@ -99,8 +99,12 @@ jobs:
|
|||||||
- name: Set up Android SDK
|
- name: Set up Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
- name: Install NDK
|
# NDK matches the version AGP resolves to (build.gradle.kts pins none, so it
|
||||||
run: yes | sdkmanager "ndk;27.2.12479018" "cmake;3.22.1" >/dev/null || true
|
# uses AGP's default 28.2.13676358); cmake 3.31.6 matches the pin in
|
||||||
|
# build.gradle.kts. 3.22.1 (AGP's default) is the shaderc deps' exact
|
||||||
|
# cmake_minimum_required floor and fails to configure spirv-tools.
|
||||||
|
- name: Install NDK + CMake
|
||||||
|
run: yes | sdkmanager "ndk;28.2.13676358" "cmake;3.31.6" >/dev/null || true
|
||||||
|
|
||||||
# shaderc's SPIRV-Tools/glslang/etc. are fetched on demand (not vendored,
|
# shaderc's SPIRV-Tools/glslang/etc. are fetched on demand (not vendored,
|
||||||
# not submodules), so `submodules: recursive` above does not provide them.
|
# not submodules), so `submodules: recursive` above does not provide them.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# iOS build — self-contained on this fork.
|
# iOS build — self-contained on this fork.
|
||||||
# Mirrors the iOS job from build-all.yml but triggers on push to ios/bring-up
|
# Produces an unsigned .ipa named with the commit SHA.
|
||||||
# (and manual dispatch), so CI runs without needing a PR to jpolo1224/pcsx2.
|
|
||||||
name: iOS Build
|
name: iOS Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -17,7 +16,7 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ios:
|
ios:
|
||||||
name: iOS (arm64, unsigned)
|
name: iOS (arm64, unsigned IPA)
|
||||||
runs-on: macos-26
|
runs-on: macos-26
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
@@ -41,9 +40,38 @@ jobs:
|
|||||||
cmake --build build --config Release
|
cmake --build build --config Release
|
||||||
-- -sdk iphonesimulator CODE_SIGNING_ALLOWED=NO
|
-- -sdk iphonesimulator CODE_SIGNING_ALLOWED=NO
|
||||||
|
|
||||||
- name: Upload .app
|
- name: Package unsigned .ipa
|
||||||
|
working-directory: platforms/ios/app/src/main/cpp
|
||||||
|
run: |
|
||||||
|
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-8)
|
||||||
|
IPA_NAME="ARMSX2-iOS-${SHORT_SHA}.ipa"
|
||||||
|
|
||||||
|
# Find the built .app (Xcode generator puts it under build/.../Release-iphonesimulator/)
|
||||||
|
APP_PATH=$(find build -name "ARMSX2iOS.app" -type d | head -1)
|
||||||
|
if [ -z "$APP_PATH" ]; then
|
||||||
|
echo "ERROR: ARMSX2iOS.app not found in build output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found app at: $APP_PATH"
|
||||||
|
echo "Packaging as: $IPA_NAME"
|
||||||
|
|
||||||
|
# Create the Payload directory structure that .ipa expects
|
||||||
|
STAGING=$(mktemp -d)
|
||||||
|
mkdir -p "$STAGING/Payload"
|
||||||
|
cp -R "$APP_PATH" "$STAGING/Payload/"
|
||||||
|
|
||||||
|
# Zip as .ipa (zip stores Payload/ at root)
|
||||||
|
cd "$STAGING"
|
||||||
|
zip -r "$GITHUB_WORKSPACE/$IPA_NAME" Payload -x "*.DS_Store"
|
||||||
|
cd "$GITHUB_WORKSPACE"
|
||||||
|
|
||||||
|
echo "IPA_NAME=$IPA_NAME" >> $GITHUB_ENV
|
||||||
|
ls -lh "$IPA_NAME"
|
||||||
|
|
||||||
|
- name: Upload .ipa
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: armsx2-ios-arm64
|
name: ${{ env.IPA_NAME }}
|
||||||
path: platforms/ios/app/src/main/cpp/build/**/*.app
|
path: ${{ github.workspace }}/${{ env.IPA_NAME }}
|
||||||
if-no-files-found: warn
|
if-no-files-found: error
|
||||||
|
|||||||
Vendored
+1
-1
@@ -125,7 +125,7 @@ if(USE_SNDIO)
|
|||||||
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
|
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
|
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
|
||||||
if(USE_AUDIOUNIT)
|
if(USE_AUDIOUNIT)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
|
|||||||
Vendored
+4
@@ -24,6 +24,10 @@ if(WIN32)
|
|||||||
src/connection_win.cpp
|
src/connection_win.cpp
|
||||||
src/discord_register_win.cpp
|
src/discord_register_win.cpp
|
||||||
)
|
)
|
||||||
|
elseif(APPLE AND IOS)
|
||||||
|
target_sources(discord-rpc PRIVATE
|
||||||
|
src/connection_unix.cpp
|
||||||
|
)
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
target_sources(discord-rpc PRIVATE
|
target_sources(discord-rpc PRIVATE
|
||||||
src/connection_unix.cpp
|
src/connection_unix.cpp
|
||||||
|
|||||||
@@ -109,6 +109,36 @@ changed yet.
|
|||||||
> way — the mobile-specific VK/GLES fixes (and the Mali/MediaTek gates) must be
|
> way — the mobile-specific VK/GLES fixes (and the Mali/MediaTek gates) must be
|
||||||
> re-applied on top of canonical GS behind `#if __ANDROID__`, device-tested. The graft
|
> re-applied on top of canonical GS behind `#if __ANDROID__`, device-tested. The graft
|
||||||
> itself is preserved in `origin/master` history (`ecfebfd6b2`) as the reference.
|
> itself is preserved in `origin/master` history (`ecfebfd6b2`) as the reference.
|
||||||
|
>
|
||||||
|
> ✅✅ 2026-07-10 (later) — **DONE, clean re-apply, device-verified on RP6 (Adreno 740 / Turnip).**
|
||||||
|
> Method: reset the GS dir to canonical, then re-applied ONLY the mobile delta as guarded
|
||||||
|
> hunks (dropped the yaps2 readback-kick perf graft entirely — it was unguarded desktop
|
||||||
|
> divergence, not needed for rendering), then audited every remaining hunk so PC/mac/Linux/
|
||||||
|
> Windows is byte-for-canonical. Touches VK (6 files) + OGL (`GSDeviceOGL`, `GLContext*`,
|
||||||
|
> `GLShaderCache`, `GSGPUProfile`).
|
||||||
|
> - **Vulkan black screen** was the missing **push-descriptor fallback**: canonical assumes
|
||||||
|
> `VK_KHR_push_descriptor` always exists; Adreno/Mali stall inside `vkCmdPushDescriptorSetKHR`,
|
||||||
|
> so textures never bind → black. Restored the capability-gated fallback (`m_use_push_descriptors`
|
||||||
|
> false only on Mali 0x13B5 / Adreno 0x5143 → per-frame descriptor-set path). Desktop keeps the
|
||||||
|
> push path byte-identical.
|
||||||
|
> - **OpenGL "boots straight back to the library"** was canonical having **no GLES path at all**;
|
||||||
|
> re-applied the GLES support (EGL context, `is_gles` shader branches, GLES query objects),
|
||||||
|
> runtime-gated by `is_gles` (false on desktop GL).
|
||||||
|
> - Found + fixed the one genuine desktop divergence in the delta: `m_features.depth_feedback`
|
||||||
|
> was force-`false` unconditionally → now `#if __ANDROID__` (desktop keeps `feedback_loops()`).
|
||||||
|
> Guarded 3 desktop-reachable OGL riders (present-path `glInvalidateFramebuffer` → `is_gles`,
|
||||||
|
> EGL `SetDisplay()` body → `#if __ANDROID__`, restored the dropped negative-swap-interval probe).
|
||||||
|
> KEPT (deliberately, they match upstream master and are in refresh-experimental): the RenderHW
|
||||||
|
> feedback-loop guard + `ProgramSelector::operator==` field-compare (#243).
|
||||||
|
> - **RA toast "giant malformed border"**: your `00bea431d` `AddRect` fix is correct for the
|
||||||
|
> desktop imgui **1.92.8** (which swapped the `thickness`/`flags` args) but the Android build
|
||||||
|
> vendors imgui **1.92.6** (pre-swap), so the same line drew a ~240px border there. Guarded that
|
||||||
|
> one call on `IMGUI_VERSION_NUM` — both platforms correct; the guard collapses to one branch
|
||||||
|
> once the two `3rdparty/imgui` copies dedup (see #4). *(This is the imgui sibling of the ryml
|
||||||
|
> shim below — same root cause: two vendored copies at different versions.)*
|
||||||
|
> - The "Graphics API is not set to Automatic" OSD warning (reintroduced by the merge) is now
|
||||||
|
> `#if !__ANDROID__` — Android forces an explicit GL/VK pick by design so it fired every boot;
|
||||||
|
> desktop keeps the canonical warning.
|
||||||
3. **arm64 JIT fixes to port** (3 real Android commits):
|
3. **arm64 JIT fixes to port** (3 real Android commits):
|
||||||
`45b4b68d10` (microVU PQ lanes), `75351e8545` (FTOI NaN / SQRT clamp),
|
`45b4b68d10` (microVU PQ lanes), `75351e8545` (FTOI NaN / SQRT clamp),
|
||||||
`ec06302ccf` (skip microVU emit on jump-cache hits).
|
`ec06302ccf` (skip microVU emit on jump-cache hits).
|
||||||
@@ -135,10 +165,31 @@ changed yet.
|
|||||||
> - **Follow-up decision still open:** whether the mac-port EE is worth folding into the
|
> - **Follow-up decision still open:** whether the mac-port EE is worth folding into the
|
||||||
> canonical JIT for *all* arm64 (bench canonical-EE+PGO vs the graft). Until then the
|
> canonical JIT for *all* arm64 (bench canonical-EE+PGO vs the graft). Until then the
|
||||||
> fork keeps the two backends cleanly separated.
|
> fork keeps the two backends cleanly separated.
|
||||||
|
>
|
||||||
|
> ✅✅ 2026-07-10 (later) — **FORK DROPPED, unified on canonical (commit `a6aa75bff`).**
|
||||||
|
> Benched it on device: A/B of canonical EE+VU + force-float vs the mac-port graft (same
|
||||||
|
> working GS, same PGO — which was tuned for the mac-port, so the test was rigged AGAINST
|
||||||
|
> canonical) → GoW2 combat **canonical EE 12.84 ms vs mac-port 11.71 ms vs refresh-exp
|
||||||
|
> 12.44 ms**, all 100% speed / 60 fps. Canonical is at refresh parity; the ~1 ms is noise
|
||||||
|
> (a canonical-tuned PGO regen closes it). Deleted the 8 `aR5900*.android.cpp` + the
|
||||||
|
> `if(ANDROID)` CMake split — Android now builds the canonical EE like every other arm64
|
||||||
|
> target. Single JIT, no divergence. The real Android EE/VU lever was always the force-float,
|
||||||
|
> not the backend.
|
||||||
4. **3rdparty de-duplication.** `platforms/android/.../cpp/3rdparty` (adrenotools
|
4. **3rdparty de-duplication.** `platforms/android/.../cpp/3rdparty` (adrenotools
|
||||||
+ others) is still vendored for the NDK build. Keep adrenotools/oboe
|
+ others) is still vendored for the NDK build. Keep adrenotools/oboe
|
||||||
(Android-only); evaluate sourcing the rest from root `3rdparty/` once the
|
(Android-only); evaluate sourcing the rest from root `3rdparty/` once the
|
||||||
NDK build is green.
|
NDK build is green.
|
||||||
|
> ⚠️→✅ 2026-07-10: the `pcsx2master` merge broke the **Android** compile — `common/YAML.cpp`
|
||||||
|
> calls `c4::yml::Callbacks::set_user_data()`, which the vendored rapidyaml **0.10.0** here
|
||||||
|
> lacks (PC/mac resolve a newer system ryml via `find_package(ryml)`). Unblocked with a
|
||||||
|
> 1-line shim: added the `set_user_data()` setter to the vendored `Callbacks` (it just sets
|
||||||
|
> the existing `m_user_data`), commit `a6aa75bff`. Proper fix = dedupe the Android build onto
|
||||||
|
> the canonical ryml so this shim can be deleted.
|
||||||
|
> ⚠️ 2026-07-10: **imgui has the exact same two-copies-different-versions problem** — desktop
|
||||||
|
> `3rdparty/imgui` = **1.92.8**, Android `platforms/android/.../cpp/3rdparty/imgui` = **1.92.6**.
|
||||||
|
> It already bit us once (the RA-toast `AddRect` arg-swap, see #2), now papered over with an
|
||||||
|
> `IMGUI_VERSION_NUM` guard. Deduping imgui to one version deletes that guard too — same task
|
||||||
|
> as the ryml dedup, worth doing together.
|
||||||
5. **`common/PNGStub.cpp`** (iOS) is relocated but not yet wired into
|
5. **`common/PNGStub.cpp`** (iOS) is relocated but not yet wired into
|
||||||
`common/CMakeLists.txt` — add under an iOS guard if the iOS build references it.
|
`common/CMakeLists.txt` — add under an iOS guard if the iOS build references it.
|
||||||
6. **Windows-on-arm64** PC build: no reusable `windows_build_qt.yml` exists on
|
6. **Windows-on-arm64** PC build: no reusable `windows_build_qt.yml` exists on
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -25,8 +26,15 @@
|
|||||||
#include <mach/task.h>
|
#include <mach/task.h>
|
||||||
#include <mach/thread_state.h>
|
#include <mach/thread_state.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
// iOS has no ApplicationServices (CGEvent mouse APIs) or IOKit pwr_mgt.
|
||||||
|
// Stub these out — iOS uses UIKit GameController, not mouse/screen-saver APIs.
|
||||||
|
#else
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Darwin (OSX) is a bit different from Linux when requesting properties of
|
// Darwin (OSX) is a bit different from Linux when requesting properties of
|
||||||
// the OS because of its BSD/Mach heritage. Helpfully, most of this code
|
// the OS because of its BSD/Mach heritage. Helpfully, most of this code
|
||||||
@@ -136,10 +144,13 @@ std::string GetOSVersionString()
|
|||||||
return type + " " + release + " " + arch;
|
return type + " " + release + " " + arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
static IOPMAssertionID s_pm_assertion;
|
static IOPMAssertionID s_pm_assertion;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool Common::InhibitScreensaver(bool inhibit)
|
bool Common::InhibitScreensaver(bool inhibit)
|
||||||
{
|
{
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
if (s_pm_assertion)
|
if (s_pm_assertion)
|
||||||
{
|
{
|
||||||
IOPMAssertionRelease(s_pm_assertion);
|
IOPMAssertionRelease(s_pm_assertion);
|
||||||
@@ -148,10 +159,16 @@ bool Common::InhibitScreensaver(bool inhibit)
|
|||||||
|
|
||||||
if (inhibit)
|
if (inhibit)
|
||||||
IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Playing a game"), &s_pm_assertion);
|
IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Playing a game"), &s_pm_assertion);
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
// iOS has no mouse cursor — stub these out.
|
||||||
|
void Common::SetMousePosition(int x, int y) {}
|
||||||
|
bool Common::AttachMousePositionCb(std::function<void(int, int)> cb) { return false; }
|
||||||
|
void Common::DetachMousePositionCb() {}
|
||||||
|
#else
|
||||||
void Common::SetMousePosition(int x, int y)
|
void Common::SetMousePosition(int x, int y)
|
||||||
{
|
{
|
||||||
// Little bit ugly but;
|
// Little bit ugly but;
|
||||||
@@ -214,6 +231,7 @@ void Common::DetachMousePositionCb()
|
|||||||
mouseRunLoopSource = nullptr;
|
mouseRunLoopSource = nullptr;
|
||||||
mouseEventTap = nullptr;
|
mouseEventTap = nullptr;
|
||||||
}
|
}
|
||||||
|
#endif // !TARGET_OS_IPHONE
|
||||||
|
|
||||||
void Threading::Sleep(int ms)
|
void Threading::Sleep(int ms)
|
||||||
{
|
{
|
||||||
@@ -308,6 +326,30 @@ const CPUInfo& GetCPUInfo()
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
// iOS JIT diagnostics stubs — Phase 3 will add the real W^X/JIT implementation.
|
||||||
|
namespace DarwinMisc {
|
||||||
|
bool iPSX2_FORCE_EE_INTERP = false;
|
||||||
|
int iPSX2_FORCE_JIT_VERIFY = 0;
|
||||||
|
int iPSX2_CALL_TGT_X9 = 0;
|
||||||
|
int iPSX2_CRASH_PACK = 0;
|
||||||
|
int iPSX2_WX_TRACE = 0;
|
||||||
|
int iPSX2_CALLPROBE = 0;
|
||||||
|
int iPSX2_JIT_HLE = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_ONLY = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_LOADSTORE = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MMI = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_COP2_VU = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MULTDIV = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_SHIFTS = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MOVES = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_INTEGER_ALU = 0;
|
||||||
|
int iPSX2_BISECT_COP1_EVERYTHING_PLUS_BRANCHES = 0;
|
||||||
|
bool IsJITAvailable() { return false; }
|
||||||
|
void SetCrashLogFD(int fd) {}
|
||||||
|
} // namespace DarwinMisc
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t HostSys::GetRuntimePageSize()
|
size_t HostSys::GetRuntimePageSize()
|
||||||
{
|
{
|
||||||
return sysctlbyname_T<u32>("hw.pagesize").value_or(0);
|
return sysctlbyname_T<u32>("hw.pagesize").value_or(0);
|
||||||
@@ -325,14 +367,22 @@ static thread_local int s_code_write_depth = 0;
|
|||||||
void HostSys::BeginCodeWrite()
|
void HostSys::BeginCodeWrite()
|
||||||
{
|
{
|
||||||
if ((s_code_write_depth++) == 0)
|
if ((s_code_write_depth++) == 0)
|
||||||
|
{
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
pthread_jit_write_protect_np(0);
|
pthread_jit_write_protect_np(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostSys::EndCodeWrite()
|
void HostSys::EndCodeWrite()
|
||||||
{
|
{
|
||||||
pxAssert(s_code_write_depth > 0);
|
pxAssert(s_code_write_depth > 0);
|
||||||
if ((--s_code_write_depth) == 0)
|
if ((--s_code_write_depth) == 0)
|
||||||
|
{
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
pthread_jit_write_protect_np(1);
|
pthread_jit_write_protect_np(1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
|
||||||
|
|||||||
@@ -19,6 +19,29 @@ struct CPUClass {
|
|||||||
|
|
||||||
std::vector<CPUClass> GetCPUClasses();
|
std::vector<CPUClass> GetCPUClasses();
|
||||||
|
|
||||||
|
// iOS JIT availability + diagnostics stubs.
|
||||||
|
// Phase 3 will replace these with the full iOS W^X/JIT implementation.
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
extern bool iPSX2_FORCE_EE_INTERP;
|
||||||
|
extern int iPSX2_FORCE_JIT_VERIFY;
|
||||||
|
extern int iPSX2_CALL_TGT_X9;
|
||||||
|
extern int iPSX2_CRASH_PACK;
|
||||||
|
extern int iPSX2_WX_TRACE;
|
||||||
|
extern int iPSX2_CALLPROBE;
|
||||||
|
extern int iPSX2_JIT_HLE;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_ONLY;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_LOADSTORE;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MMI;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_COP2_VU;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MULTDIV;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_SHIFTS;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_MOVES;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_INTEGER_ALU;
|
||||||
|
extern int iPSX2_BISECT_COP1_EVERYTHING_PLUS_BRANCHES;
|
||||||
|
bool IsJITAvailable();
|
||||||
|
void SetCrashLogFD(int fd);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -145,7 +145,11 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
|
|||||||
|
|
||||||
// Callbacks passed to ryml::Tree are used for value parsing errors later,
|
// Callbacks passed to ryml::Tree are used for value parsing errors later,
|
||||||
// so we need to clear the context before it goes out of scope.
|
// so we need to clear the context before it goes out of scope.
|
||||||
|
#if RYML_VERSION_MAJOR > 0 || RYML_VERSION_MINOR >= 11
|
||||||
callbacks.set_user_data(nullptr);
|
callbacks.set_user_data(nullptr);
|
||||||
|
#else
|
||||||
|
callbacks.m_user_data = nullptr;
|
||||||
|
#endif
|
||||||
tree.callbacks(callbacks);
|
tree.callbacks(callbacks);
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
|
|||||||
@@ -248,6 +248,10 @@ namespace Achievements
|
|||||||
static std::optional<AchievementProgressIndicator> s_active_progress_indicator;
|
static std::optional<AchievementProgressIndicator> s_active_progress_indicator;
|
||||||
} // namespace Achievements
|
} // namespace Achievements
|
||||||
|
|
||||||
|
bool Achievements::GetCurrentUserStats(UserStats*) { return false; }
|
||||||
|
bool Achievements::GetCurrentGameStats(GameStats*) { return false; }
|
||||||
|
bool Achievements::GetCurrentAchievementList(std::vector<AchievementInfo>*) { return false; }
|
||||||
|
|
||||||
|
|
||||||
std::unique_lock<std::recursive_mutex> Achievements::GetLock()
|
std::unique_lock<std::recursive_mutex> Achievements::GetLock()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -182,6 +182,54 @@ namespace Achievements
|
|||||||
void ActivateMenuItem(int item);
|
void ActivateMenuItem(int item);
|
||||||
} // namespace RAIntegration
|
} // namespace RAIntegration
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct UserStats
|
||||||
|
{
|
||||||
|
std::string username;
|
||||||
|
std::string display_name;
|
||||||
|
std::string avatar_path;
|
||||||
|
u32 points = 0;
|
||||||
|
u32 softcore_points = 0;
|
||||||
|
u32 unread_messages = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GameStats
|
||||||
|
{
|
||||||
|
std::string title;
|
||||||
|
std::string rich_presence;
|
||||||
|
std::string icon_path;
|
||||||
|
std::string icon_url;
|
||||||
|
u32 game_id = 0;
|
||||||
|
u32 unlocked_achievements = 0;
|
||||||
|
u32 total_achievements = 0;
|
||||||
|
u32 unlocked_points = 0;
|
||||||
|
u32 total_points = 0;
|
||||||
|
bool has_achievements = false;
|
||||||
|
bool has_leaderboards = false;
|
||||||
|
bool has_rich_presence = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AchievementInfo
|
||||||
|
{
|
||||||
|
std::string title;
|
||||||
|
std::string description;
|
||||||
|
std::string badge_path;
|
||||||
|
std::string measured_progress;
|
||||||
|
u32 id = 0;
|
||||||
|
u32 points = 0;
|
||||||
|
u32 unlock_time = 0;
|
||||||
|
u32 state = 0;
|
||||||
|
u32 category = 0;
|
||||||
|
u32 bucket = 0;
|
||||||
|
u32 unlocked = 0;
|
||||||
|
float measured_percent = 0.0f;
|
||||||
|
float rarity = 0.0f;
|
||||||
|
float rarity_hardcore = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool GetCurrentUserStats(UserStats* stats);
|
||||||
|
bool GetCurrentGameStats(GameStats* stats);
|
||||||
|
bool GetCurrentAchievementList(std::vector<AchievementInfo>* achievements);
|
||||||
} // namespace Achievements
|
} // namespace Achievements
|
||||||
|
|
||||||
/// Functions implemented in the frontend.
|
/// Functions implemented in the frontend.
|
||||||
|
|||||||
+29
-32
@@ -722,7 +722,7 @@ elseif(LINUX)
|
|||||||
list(APPEND pcsx2USBHeaders
|
list(APPEND pcsx2USBHeaders
|
||||||
USB/usb-eyetoy/cam-linux.h
|
USB/usb-eyetoy/cam-linux.h
|
||||||
)
|
)
|
||||||
elseif(APPLE)
|
elseif(APPLE AND NOT IOS)
|
||||||
list(APPEND pcsx2USBSources
|
list(APPEND pcsx2USBSources
|
||||||
USB/usb-eyetoy/cam-macos.mm
|
USB/usb-eyetoy/cam-macos.mm
|
||||||
)
|
)
|
||||||
@@ -1061,34 +1061,22 @@ set(pcsx2x86Headers
|
|||||||
|
|
||||||
# ARM64
|
# ARM64
|
||||||
#
|
#
|
||||||
# EE recompiler (aR5900*) is forked: the canonical Phase-7 backend builds for
|
# EE recompiler (aR5900*): a SINGLE canonical Phase-7 backend for every arm64 target
|
||||||
# PC (macOS/Linux/Windows arm64); Android builds the mac-port graft variants
|
# (PC macOS/Linux/Windows + Android). The Android mac-port graft fork was dropped after a
|
||||||
# (arm64/aR5900*.android.cpp) which carry the Android-only EE codegen wins.
|
# device A/B (canonical EE+VU + force-float, PGO tuned for the mac-port so rigged against
|
||||||
# Both share the canonical aR5900.h/aR5900Analysis.h (identical interface).
|
# canonical) showed canonical EE at refresh parity on Android — GoW2 combat EE ~12.8ms,
|
||||||
# Keep the two lists in lockstep. See REFACTOR_STATUS.md.
|
# 100% speed / 60fps. The real Android perf lever is the force-float in
|
||||||
if(ANDROID)
|
# VMManager::SetEmuThreadAffinities, not the recompiler backend. See REFACTOR_STATUS.md.
|
||||||
set(pcsx2arm64EESources
|
set(pcsx2arm64EESources
|
||||||
arm64/aR5900.android.cpp
|
arm64/aR5900.cpp
|
||||||
arm64/aR5900Analysis.android.cpp
|
arm64/aR5900Analysis.cpp
|
||||||
arm64/aR5900LoadStore.android.cpp
|
arm64/aR5900LoadStore.cpp
|
||||||
arm64/aR5900Arith.android.cpp
|
arm64/aR5900Arith.cpp
|
||||||
arm64/aR5900MultDiv.android.cpp
|
arm64/aR5900MultDiv.cpp
|
||||||
arm64/aR5900Branch.android.cpp
|
arm64/aR5900Branch.cpp
|
||||||
arm64/aR5900FPU.android.cpp
|
arm64/aR5900FPU.cpp
|
||||||
arm64/aR5900MMI.android.cpp
|
arm64/aR5900MMI.cpp
|
||||||
)
|
)
|
||||||
else()
|
|
||||||
set(pcsx2arm64EESources
|
|
||||||
arm64/aR5900.cpp
|
|
||||||
arm64/aR5900Analysis.cpp
|
|
||||||
arm64/aR5900LoadStore.cpp
|
|
||||||
arm64/aR5900Arith.cpp
|
|
||||||
arm64/aR5900MultDiv.cpp
|
|
||||||
arm64/aR5900Branch.cpp
|
|
||||||
arm64/aR5900FPU.cpp
|
|
||||||
arm64/aR5900MMI.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(pcsx2arm64Sources
|
set(pcsx2arm64Sources
|
||||||
arm64/AsmHelpers.cpp
|
arm64/AsmHelpers.cpp
|
||||||
@@ -1258,10 +1246,10 @@ endif()
|
|||||||
target_sources(PCSX2 PRIVATE ${pcsx2USBSources} ${pcsx2USBHeaders})
|
target_sources(PCSX2 PRIVATE ${pcsx2USBSources} ${pcsx2USBHeaders})
|
||||||
|
|
||||||
if(APPLE OR BSD)
|
if(APPLE OR BSD)
|
||||||
if(APPLE)
|
if(APPLE AND NOT IOS)
|
||||||
target_sources(PCSX2 PRIVATE
|
target_sources(PCSX2 PRIVATE
|
||||||
${pcsx2OSXSources})
|
${pcsx2OSXSources})
|
||||||
else()
|
elseif(BSD)
|
||||||
target_sources(PCSX2 PRIVATE
|
target_sources(PCSX2 PRIVATE
|
||||||
${pcsx2FreeBSDSources})
|
${pcsx2FreeBSDSources})
|
||||||
endif()
|
endif()
|
||||||
@@ -1373,7 +1361,8 @@ fixup_file_properties(PCSX2)
|
|||||||
# To ensure the dependency build script's headers are preferred, push any directories that look like */local/include to the end.
|
# To ensure the dependency build script's headers are preferred, push any directories that look like */local/include to the end.
|
||||||
force_include_last(PCSX2_FLAGS "/(usr|local)/include/?$")
|
force_include_last(PCSX2_FLAGS "/(usr|local)/include/?$")
|
||||||
|
|
||||||
if (APPLE)
|
if(APPLE AND NOT IOS)
|
||||||
|
# macOS desktop: AppKit + IOKit are available.
|
||||||
find_library(APPKIT_LIBRARY AppKit)
|
find_library(APPKIT_LIBRARY AppKit)
|
||||||
find_library(IOKIT_LIBRARY IOKit)
|
find_library(IOKIT_LIBRARY IOKit)
|
||||||
find_library(METAL_LIBRARY Metal)
|
find_library(METAL_LIBRARY Metal)
|
||||||
@@ -1391,6 +1380,14 @@ if (APPLE)
|
|||||||
# MetalFX (spatial upscaler) is macOS 13.0+. Weak-link it so the binary still
|
# MetalFX (spatial upscaler) is macOS 13.0+. Weak-link it so the binary still
|
||||||
# loads on older systems; every use is guarded with @available(macOS 13, *).
|
# loads on older systems; every use is guarded with @available(macOS 13, *).
|
||||||
target_link_options(PCSX2_FLAGS INTERFACE "SHELL:-weak_framework MetalFX")
|
target_link_options(PCSX2_FLAGS INTERFACE "SHELL:-weak_framework MetalFX")
|
||||||
|
elseif(IOS)
|
||||||
|
# iOS: Metal + QuartzCore (CAMetalLayer) are available; AppKit/IOKit are not.
|
||||||
|
find_library(METAL_LIBRARY Metal)
|
||||||
|
find_library(QUARTZCORE_LIBRARY QuartzCore)
|
||||||
|
target_link_libraries(PCSX2_FLAGS INTERFACE
|
||||||
|
${METAL_LIBRARY}
|
||||||
|
${QUARTZCORE_LIBRARY}
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY PCSX2_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
set_property(GLOBAL PROPERTY PCSX2_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined(__FreeBSD__) || (__APPLE__)
|
#include <TargetConditionals.h>
|
||||||
|
#if (defined(__FreeBSD__) || (__APPLE__)) && !TARGET_OS_IPHONE
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
@@ -225,27 +226,46 @@ bool AdapterUtils::GetAdapterAuto(Adapter* adapter, AdapterBuffer* buffer)
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
|
if ((pAdapter->ifa_flags & IFF_LOOPBACK) == 0 &&
|
||||||
(pAdapter->ifa_flags & IFF_UP) != 0)
|
(pAdapter->ifa_flags & IFF_UP) != 0 &&
|
||||||
|
pAdapter->ifa_addr != nullptr &&
|
||||||
|
ReadAddressFamily(pAdapter->ifa_addr) == AF_INET)
|
||||||
{
|
{
|
||||||
// Search for an adapter with;
|
// Search for an adapter with;
|
||||||
// IPv4 Address,
|
// IPv4 Address,
|
||||||
// Gateway.
|
// Gateway.
|
||||||
|
|
||||||
bool hasIPv4 = false;
|
bool hasIPv4 = false;
|
||||||
|
#if !defined(__APPLE__) || !TARGET_OS_IPHONE
|
||||||
bool hasGateway = false;
|
bool hasGateway = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (GetAdapterIP(pAdapter).has_value())
|
if (GetAdapterIP(pAdapter).has_value())
|
||||||
hasIPv4 = true;
|
hasIPv4 = true;
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) || !TARGET_OS_IPHONE
|
||||||
if (GetGateways(pAdapter).size() > 0)
|
if (GetGateways(pAdapter).size() > 0)
|
||||||
hasGateway = true;
|
hasGateway = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && TARGET_OS_IPHONE
|
||||||
|
// iOS does not expose the desktop/macOS route sysctl path used by
|
||||||
|
// GetGateways(), but sockets mode only needs a usable IPv4
|
||||||
|
// interface here. The internal DHCP gateway is injected later.
|
||||||
|
if (hasIPv4)
|
||||||
|
{
|
||||||
|
Console.WriteLn("DEV9: Socket: iOS Auto selected adapter '%s' without gateway probe", pAdapter->ifa_name);
|
||||||
|
*adapter = *pAdapter;
|
||||||
|
buffer->swap(adapterInfo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (hasIPv4 && hasGateway)
|
if (hasIPv4 && hasGateway)
|
||||||
{
|
{
|
||||||
*adapter = *pAdapter;
|
*adapter = *pAdapter;
|
||||||
buffer->swap(adapterInfo);
|
buffer->swap(adapterInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
pAdapter = pAdapter->ifa_next;
|
pAdapter = pAdapter->ifa_next;
|
||||||
@@ -268,6 +288,11 @@ std::optional<MAC_Address> AdapterUtils::GetAdapterMAC(const Adapter* adapter)
|
|||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
#elif defined(__APPLE__) && TARGET_OS_IPHONE
|
||||||
|
std::optional<MAC_Address> AdapterUtils::GetAdapterMAC(const Adapter* adapter)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
std::optional<MAC_Address> AdapterUtils::GetAdapterMAC(const Adapter* adapter)
|
std::optional<MAC_Address> AdapterUtils::GetAdapterMAC(const Adapter* adapter)
|
||||||
{
|
{
|
||||||
@@ -436,7 +461,7 @@ std::vector<IP_Address> AdapterUtils::GetGateways(const Adapter* adapter)
|
|||||||
}
|
}
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
#elif defined(__FreeBSD__) || defined(__APPLE__)
|
#elif (defined(__FreeBSD__) || defined(__APPLE__)) && !TARGET_OS_IPHONE
|
||||||
std::vector<IP_Address> AdapterUtils::GetGateways(const Adapter* adapter)
|
std::vector<IP_Address> AdapterUtils::GetGateways(const Adapter* adapter)
|
||||||
{
|
{
|
||||||
if (adapter == nullptr)
|
if (adapter == nullptr)
|
||||||
@@ -518,7 +543,7 @@ std::vector<IP_Address> AdapterUtils::GetGateways(const Adapter* adapter)
|
|||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
std::vector<IP_Address> AdapterUtils::GetGateways(Adapter* adapter)
|
std::vector<IP_Address> AdapterUtils::GetGateways(const Adapter* adapter)
|
||||||
{
|
{
|
||||||
Console.Error("DEV9: Unsupported OS, can't find Gateway");
|
Console.Error("DEV9: Unsupported OS, can't find Gateway");
|
||||||
return {};
|
return {};
|
||||||
@@ -567,8 +592,17 @@ std::vector<IP_Address> AdapterUtils::GetDNS(const Adapter* adapter)
|
|||||||
if (servers.fail())
|
if (servers.fail())
|
||||||
{
|
{
|
||||||
servers.close();
|
servers.close();
|
||||||
|
#if defined(__APPLE__) && TARGET_OS_IPHONE
|
||||||
|
// iOS app sandbox has no /etc/resolv.conf; fall back to public resolvers so the
|
||||||
|
// emulated PS2 gets a working DNS list. Overrideable via Network settings.
|
||||||
|
Console.WriteLn("DEV9: no /etc/resolv.conf on iOS; using public DNS fallback 1.1.1.1 / 8.8.8.8");
|
||||||
|
collection.push_back(IP_Address{{{1, 1, 1, 1}}});
|
||||||
|
collection.push_back(IP_Address{{{8, 8, 8, 8}}});
|
||||||
|
return collection;
|
||||||
|
#else
|
||||||
Console.Error("DEV9: Failed to open /etc/resolv.conf");
|
Console.Error("DEV9: Failed to open /etc/resolv.conf");
|
||||||
return collection;
|
return collection;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#if defined(__FreeBSD__) || (__APPLE__)
|
#if (defined(__FreeBSD__) || (__APPLE__)) && !TARGET_OS_IPHONE
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|||||||
@@ -114,6 +114,37 @@ static bool LooksLikeMali(std::string_view lowered_hints)
|
|||||||
// on Mali-specific markers. "valhall"/"bifrost"/"midgard" are Mali GPU arches.
|
// on Mali-specific markers. "valhall"/"bifrost"/"midgard" are Mali GPU arches.
|
||||||
return ContainsAny(lowered_hints, {"mali", "valhall", "bifrost", "midgard"});
|
return ContainsAny(lowered_hints, {"mali", "valhall", "bifrost", "midgard"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool LooksLikeMediaTekSoc(std::string_view lowered_hints)
|
||||||
|
{
|
||||||
|
// Ported from sashkinbro/EmuCoreX. Detect MediaTek (Dimensity/Helio) SoCs so
|
||||||
|
// we can disable the broken Vulkan fbfetch path on their Mali stacks. The SoC
|
||||||
|
// props (ro.soc.manufacturer/model/platform) are already folded into the hints
|
||||||
|
// string by BuildHints().
|
||||||
|
if (ContainsAny(lowered_hints, {"mediatek", "dimensity", "helio", "mtk"}))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// MediaTek board/platform properties commonly use compact part numbers such as
|
||||||
|
// mt6877 or mt6989z without spelling out the vendor. Require a token boundary and
|
||||||
|
// four digits so an unrelated "mt" isn't treated as a chipset id.
|
||||||
|
for (size_t i = 0; i + 6 <= lowered_hints.size(); i++)
|
||||||
|
{
|
||||||
|
if (lowered_hints[i] != 'm' || lowered_hints[i + 1] != 't' ||
|
||||||
|
(i > 0 && std::isalnum(static_cast<unsigned char>(lowered_hints[i - 1]))))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_four_digits = true;
|
||||||
|
for (size_t digit = i + 2; digit < i + 6; digit++)
|
||||||
|
has_four_digits &= (std::isdigit(static_cast<unsigned char>(lowered_hints[digit])) != 0);
|
||||||
|
|
||||||
|
if (has_four_digits)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
GpuProfileOverride GpuProfileDetector::ParseOverride(std::string_view value)
|
GpuProfileOverride GpuProfileDetector::ParseOverride(std::string_view value)
|
||||||
@@ -181,6 +212,9 @@ GpuProfileSelection GpuProfileDetector::Resolve(std::string_view override_value,
|
|||||||
GpuProfileSelection selection;
|
GpuProfileSelection selection;
|
||||||
selection.override_mode = ParseOverride(override_value);
|
selection.override_mode = ParseOverride(override_value);
|
||||||
selection.hints = BuildHints(gpu_vendor, gpu_renderer_or_name);
|
selection.hints = BuildHints(gpu_vendor, gpu_renderer_or_name);
|
||||||
|
// Detected from the SoC hints regardless of any GPU-profile override — the
|
||||||
|
// MediaTek-Mali Vulkan fbfetch breakage is orthogonal to the Mali/Adreno profile.
|
||||||
|
selection.is_mediatek_soc = LooksLikeMediaTekSoc(ToLowerASCII(selection.hints));
|
||||||
|
|
||||||
if (selection.override_mode == GpuProfileOverride::Mali)
|
if (selection.override_mode == GpuProfileOverride::Mali)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ struct GpuProfileSelection
|
|||||||
{
|
{
|
||||||
GpuProfileOverride override_mode = GpuProfileOverride::Auto;
|
GpuProfileOverride override_mode = GpuProfileOverride::Auto;
|
||||||
RuntimeGpuProfile runtime_profile = RuntimeGpuProfile::Adreno;
|
RuntimeGpuProfile runtime_profile = RuntimeGpuProfile::Adreno;
|
||||||
|
// True when the SoC hints look like a MediaTek chipset (Dimensity/Helio). Used
|
||||||
|
// to disable the Vulkan framebuffer-fetch/ROAA path on MediaTek Mali stacks,
|
||||||
|
// whose driver returns zero/stale destination color (black or missing textures)
|
||||||
|
// across GPU generations. Ported from sashkinbro/EmuCoreX.
|
||||||
|
bool is_mediatek_soc = false;
|
||||||
std::string hints;
|
std::string hints;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,12 @@
|
|||||||
#include "GS/GS.h"
|
#include "GS/GS.h"
|
||||||
#include "GSMTLDeviceInfo.h"
|
#include "GSMTLDeviceInfo.h"
|
||||||
#include "GSMTLSharedHeader.h"
|
#include "GSMTLSharedHeader.h"
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
#include <AppKit/AppKit.h>
|
#include <AppKit/AppKit.h>
|
||||||
#include <Metal/Metal.h>
|
|
||||||
#include <MetalFX/MetalFX.h>
|
#include <MetalFX/MetalFX.h>
|
||||||
|
#endif
|
||||||
|
#include <Metal/Metal.h>
|
||||||
#include <QuartzCore/QuartzCore.h>
|
#include <QuartzCore/QuartzCore.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -225,7 +228,9 @@ public:
|
|||||||
MTLResourceOptions m_resource_options_shared_wc;
|
MTLResourceOptions m_resource_options_shared_wc;
|
||||||
|
|
||||||
// Previously in MetalHostDisplay.
|
// Previously in MetalHostDisplay.
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
MRCOwned<NSView*> m_view;
|
MRCOwned<NSView*> m_view;
|
||||||
|
#endif
|
||||||
MRCOwned<CAMetalLayer*> m_layer;
|
MRCOwned<CAMetalLayer*> m_layer;
|
||||||
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
|
MRCOwned<id<CAMetalDrawable>> m_current_drawable;
|
||||||
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
|
MRCOwned<MTLRenderPassDescriptor*> m_pass_desc;
|
||||||
@@ -253,7 +258,9 @@ public:
|
|||||||
|
|
||||||
// MetalFX spatial upscaler. Creating the scaler is expensive, so it's cached and
|
// MetalFX spatial upscaler. Creating the scaler is expensive, so it's cached and
|
||||||
// only rebuilt when the input/output size or format changes (the cache key below).
|
// only rebuilt when the input/output size or format changes (the cache key below).
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
API_AVAILABLE(macos(13.0)) MRCOwned<id<MTLFXSpatialScaler>> m_mfx_spatial;
|
API_AVAILABLE(macos(13.0)) MRCOwned<id<MTLFXSpatialScaler>> m_mfx_spatial;
|
||||||
|
#endif
|
||||||
int m_mfx_in_w = 0, m_mfx_in_h = 0, m_mfx_out_w = 0, m_mfx_out_h = 0;
|
int m_mfx_in_w = 0, m_mfx_in_h = 0, m_mfx_out_w = 0, m_mfx_out_h = 0;
|
||||||
MTLPixelFormat m_mfx_in_fmt = MTLPixelFormatInvalid, m_mfx_out_fmt = MTLPixelFormatInvalid;
|
MTLPixelFormat m_mfx_in_fmt = MTLPixelFormatInvalid, m_mfx_out_fmt = MTLPixelFormatInvalid;
|
||||||
std::vector<MRCOwned<id<MTLRenderPipelineState>>> m_convert_pipeline;
|
std::vector<MRCOwned<id<MTLRenderPipelineState>>> m_convert_pipeline;
|
||||||
|
|||||||
@@ -782,6 +782,7 @@ bool GSDeviceMTL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
|
|||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
bool GSDeviceMTL::EnsureMetalFXSpatial(GSTexture* sTex, GSTexture* dTex)
|
bool GSDeviceMTL::EnsureMetalFXSpatial(GSTexture* sTex, GSTexture* dTex)
|
||||||
{ @autoreleasepool {
|
{ @autoreleasepool {
|
||||||
id<MTLTexture> src = static_cast<GSTextureMTL*>(sTex)->GetTexture();
|
id<MTLTexture> src = static_cast<GSTextureMTL*>(sTex)->GetTexture();
|
||||||
@@ -839,6 +840,10 @@ bool GSDeviceMTL::DoMetalFXSpatial(GSTexture* sTex, GSTexture* dTex)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}}
|
}}
|
||||||
|
#else
|
||||||
|
bool GSDeviceMTL::EnsureMetalFXSpatial(GSTexture*, GSTexture*) { return false; }
|
||||||
|
bool GSDeviceMTL::DoMetalFXSpatial(GSTexture*, GSTexture*) { return false; }
|
||||||
|
#endif
|
||||||
|
|
||||||
MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
|
MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
|
||||||
{
|
{
|
||||||
@@ -937,17 +942,21 @@ void GSDeviceMTL::AttachSurfaceOnMainThread()
|
|||||||
m_layer = MRCRetain([CAMetalLayer layer]);
|
m_layer = MRCRetain([CAMetalLayer layer]);
|
||||||
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
|
[m_layer setDrawableSize:CGSizeMake(m_window_info.surface_width, m_window_info.surface_height)];
|
||||||
[m_layer setDevice:m_dev.dev];
|
[m_layer setDevice:m_dev.dev];
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
|
m_view = MRCRetain((__bridge NSView*)m_window_info.window_handle);
|
||||||
[m_view setWantsLayer:YES];
|
[m_view setWantsLayer:YES];
|
||||||
[m_view setLayer:m_layer];
|
[m_view setLayer:m_layer];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceMTL::DetachSurfaceOnMainThread()
|
void GSDeviceMTL::DetachSurfaceOnMainThread()
|
||||||
{
|
{
|
||||||
pxAssert([NSThread isMainThread]);
|
pxAssert([NSThread isMainThread]);
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
[m_view setLayer:nullptr];
|
[m_view setLayer:nullptr];
|
||||||
[m_view setWantsLayer:NO];
|
[m_view setWantsLayer:NO];
|
||||||
m_view = nullptr;
|
m_view = nullptr;
|
||||||
|
#endif
|
||||||
m_layer = nullptr;
|
m_layer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1113,7 +1122,9 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
|||||||
|
|
||||||
// Metal does not support mailbox.
|
// Metal does not support mailbox.
|
||||||
m_vsync_mode = (m_vsync_mode == GSVSyncMode::Mailbox) ? GSVSyncMode::FIFO : m_vsync_mode;
|
m_vsync_mode = (m_vsync_mode == GSVSyncMode::Mailbox) ? GSVSyncMode::FIFO : m_vsync_mode;
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
[m_layer setDisplaySyncEnabled:m_vsync_mode == GSVSyncMode::FIFO];
|
[m_layer setDisplaySyncEnabled:m_vsync_mode == GSVSyncMode::FIFO];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1139,8 +1150,10 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
|||||||
m_features.test_and_sample_depth = true;
|
m_features.test_and_sample_depth = true;
|
||||||
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
|
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
|
||||||
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
|
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
if (@available(macOS 13.0, *))
|
if (@available(macOS 13.0, *))
|
||||||
m_features.metalfx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:m_dev.dev];
|
m_features.metalfx_spatial = [MTLFXSpatialScalerDescriptor supportsDevice:m_dev.dev];
|
||||||
|
#endif
|
||||||
m_features.rov = m_dev.features.rov && !m_features.framebuffer_fetch;
|
m_features.rov = m_dev.features.rov && !m_features.framebuffer_fetch;
|
||||||
m_max_texture_size = m_dev.features.max_texsize;
|
m_max_texture_size = m_dev.features.max_texsize;
|
||||||
|
|
||||||
@@ -1577,9 +1590,11 @@ void GSDeviceMTL::EndPresent()
|
|||||||
if (!frames)
|
if (!frames)
|
||||||
{
|
{
|
||||||
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
||||||
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
|
Console.WriteLn("Metal Trace Capture to /tmp/PCSX2MTLCapture.gputrace finished");
|
||||||
[[NSWorkspace sharedWorkspace] selectFile:path
|
#if !TARGET_OS_IPHONE
|
||||||
inFileViewerRootedAtPath:@"/tmp/"];
|
[[NSWorkspace sharedWorkspace] selectFile:path
|
||||||
|
inFileViewerRootedAtPath:@"/tmp/"];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (s_capture_next)
|
else if (s_capture_next)
|
||||||
@@ -1623,7 +1638,9 @@ void GSDeviceMTL::SetVSyncMode(GSVSyncMode mode, bool allow_present_throttle)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_vsync_mode = (mode == GSVSyncMode::Mailbox) ? GSVSyncMode::FIFO : mode;
|
m_vsync_mode = (mode == GSVSyncMode::Mailbox) ? GSVSyncMode::FIFO : mode;
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
[m_layer setDisplaySyncEnabled:m_vsync_mode == GSVSyncMode::FIFO];
|
[m_layer setDisplaySyncEnabled:m_vsync_mode == GSVSyncMode::FIFO];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSDeviceMTL::SetGPUTimingEnabled(bool enabled)
|
bool GSDeviceMTL::SetGPUTimingEnabled(bool enabled)
|
||||||
@@ -2215,9 +2232,17 @@ void GSDeviceMTL::MRESetSampler(SamplerSelector sel)
|
|||||||
|
|
||||||
static void textureBarrier(id<MTLRenderCommandEncoder> enc)
|
static void textureBarrier(id<MTLRenderCommandEncoder> enc)
|
||||||
{
|
{
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#if !TARGET_OS_SIMULATOR
|
||||||
|
[enc memoryBarrierWithScope:MTLBarrierScopeTextures
|
||||||
|
afterStages:MTLRenderStageFragment
|
||||||
|
beforeStages:MTLRenderStageFragment];
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
[enc memoryBarrierWithScope:MTLBarrierScopeRenderTargets
|
[enc memoryBarrierWithScope:MTLBarrierScopeRenderTargets
|
||||||
afterStages:MTLRenderStageFragment
|
afterStages:MTLRenderStageFragment
|
||||||
beforeStages:MTLRenderStageFragment];
|
beforeStages:MTLRenderStageFragment];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
|
void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
|
||||||
|
|||||||
@@ -243,8 +243,10 @@ u32 GSMTLDevice::GetMaxTextureSize(id<MTLDevice> dev)
|
|||||||
if ([dev supportsFamily:MTLGPUFamilyApple3])
|
if ([dev supportsFamily:MTLGPUFamilyApple3])
|
||||||
return 16384;
|
return 16384;
|
||||||
}
|
}
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
|
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
|
||||||
return 16384;
|
return 16384;
|
||||||
|
#endif
|
||||||
return 8192;
|
return 8192;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0+
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
|
||||||
#include "GS/Renderers/OpenGL/GLContext.h"
|
#include "GS/Renderers/OpenGL/GLContext.h"
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include "GS/Renderers/OpenGL/GLContextEGLAndroid.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "GS/Renderers/OpenGL/GLContextWGL.h"
|
#include "GS/Renderers/OpenGL/GLContextWGL.h"
|
||||||
@@ -19,6 +22,12 @@
|
|||||||
|
|
||||||
#include "glad/gl.h"
|
#include "glad/gl.h"
|
||||||
|
|
||||||
|
static bool ShouldPreferESContext()
|
||||||
|
{
|
||||||
|
const char* value = std::getenv("PREFER_GLES_CONTEXT");
|
||||||
|
return (value && std::strcmp(value, "1") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
GLContext::GLContext(const WindowInfo& wi)
|
GLContext::GLContext(const WindowInfo& wi)
|
||||||
: m_wi(wi)
|
: m_wi(wi)
|
||||||
{
|
{
|
||||||
@@ -28,31 +37,57 @@ GLContext::~GLContext() = default;
|
|||||||
|
|
||||||
std::unique_ptr<GLContext> GLContext::Create(const WindowInfo& wi, Error* error)
|
std::unique_ptr<GLContext> GLContext::Create(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
// We need at least GL3.3.
|
|
||||||
static constexpr Version vlist[] = {
|
static constexpr Version vlist[] = {
|
||||||
{4, 6},
|
{Profile::Core, 4, 6},
|
||||||
{4, 5},
|
{Profile::Core, 4, 5},
|
||||||
{4, 4},
|
{Profile::Core, 4, 4},
|
||||||
{4, 3},
|
{Profile::Core, 4, 3},
|
||||||
{4, 2},
|
{Profile::Core, 4, 2},
|
||||||
{4, 1},
|
{Profile::Core, 4, 1},
|
||||||
{4, 0},
|
{Profile::Core, 4, 0},
|
||||||
{3, 3},
|
{Profile::Core, 3, 3},
|
||||||
|
{Profile::ES, 3, 2},
|
||||||
|
{Profile::ES, 3, 1},
|
||||||
|
{Profile::ES, 3, 0},
|
||||||
|
{Profile::ES, 2, 0},
|
||||||
};
|
};
|
||||||
|
static constexpr size_t num_versions = std::size(vlist);
|
||||||
|
|
||||||
|
const Version* versions_to_try = vlist;
|
||||||
|
size_t num_versions_to_try = num_versions;
|
||||||
|
|
||||||
|
Version reordered[num_versions];
|
||||||
|
if (ShouldPreferESContext())
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i < num_versions_to_try; i++)
|
||||||
|
{
|
||||||
|
if (versions_to_try[i].profile == Profile::ES)
|
||||||
|
reordered[count++] = versions_to_try[i];
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < num_versions_to_try; i++)
|
||||||
|
{
|
||||||
|
if (versions_to_try[i].profile != Profile::ES)
|
||||||
|
reordered[count++] = versions_to_try[i];
|
||||||
|
}
|
||||||
|
versions_to_try = reordered;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<GLContext> context;
|
std::unique_ptr<GLContext> context;
|
||||||
Error local_error;
|
#ifdef __ANDROID__
|
||||||
#if defined(_WIN32)
|
if (wi.type == WindowInfo::Type::Android)
|
||||||
context = GLContextWGL::Create(wi, vlist, error);
|
context = GLContextEGLAndroid::Create(wi, versions_to_try, num_versions_to_try);
|
||||||
#else // Linux
|
|
||||||
#if defined(X11_API)
|
|
||||||
if (wi.type == WindowInfo::Type::X11)
|
|
||||||
context = GLContextEGLX11::Create(wi, vlist, error);
|
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(_WIN32)
|
||||||
#if defined(WAYLAND_API)
|
context = GLContextWGL::Create(wi, std::span<const Version>(versions_to_try, num_versions_to_try), error);
|
||||||
if (wi.type == WindowInfo::Type::Wayland)
|
#else
|
||||||
context = GLContextEGLWayland::Create(wi, vlist, error);
|
#ifdef X11_API
|
||||||
|
if (wi.type == WindowInfo::Type::X11)
|
||||||
|
context = GLContextEGLX11::Create(wi, std::span<const Version>(versions_to_try, num_versions_to_try), error);
|
||||||
|
#endif
|
||||||
|
#ifdef WAYLAND_API
|
||||||
|
if (!context && wi.type == WindowInfo::Type::Wayland)
|
||||||
|
context = GLContextEGLWayland::Create(wi, std::span<const Version>(versions_to_try, num_versions_to_try), error);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -63,11 +98,21 @@ std::unique_ptr<GLContext> GLContext::Create(const WindowInfo& wi, Error* error)
|
|||||||
static GLContext* context_being_created;
|
static GLContext* context_being_created;
|
||||||
context_being_created = context.get();
|
context_being_created = context.get();
|
||||||
|
|
||||||
// load up glad
|
if (!context->IsGLES())
|
||||||
if (!gladLoadGL([](const char* name) { return reinterpret_cast<GLADapiproc>(context_being_created->GetProcAddress(name)); }))
|
|
||||||
{
|
{
|
||||||
Error::SetStringView(error, "Failed to load GL functions for GLAD");
|
if (!gladLoadGL([](const char* name) { return reinterpret_cast<GLADapiproc>(context_being_created->GetProcAddress(name)); }))
|
||||||
return nullptr;
|
{
|
||||||
|
Error::SetStringView(error, "Failed to load GL functions for GLAD");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!gladLoadGLES2([](const char* name) { return reinterpret_cast<GLADapiproc>(context_being_created->GetProcAddress(name)); }))
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Failed to load GLES functions for GLAD");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context_being_created = nullptr;
|
context_being_created = nullptr;
|
||||||
|
|||||||
@@ -18,13 +18,22 @@ public:
|
|||||||
GLContext(const WindowInfo& wi);
|
GLContext(const WindowInfo& wi);
|
||||||
virtual ~GLContext();
|
virtual ~GLContext();
|
||||||
|
|
||||||
|
enum class Profile
|
||||||
|
{
|
||||||
|
NoProfile,
|
||||||
|
Core,
|
||||||
|
ES
|
||||||
|
};
|
||||||
|
|
||||||
struct Version
|
struct Version
|
||||||
{
|
{
|
||||||
|
Profile profile;
|
||||||
int major_version;
|
int major_version;
|
||||||
int minor_version;
|
int minor_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
__fi const WindowInfo& GetWindowInfo() const { return m_wi; }
|
__fi const WindowInfo& GetWindowInfo() const { return m_wi; }
|
||||||
|
__fi bool IsGLES() const { return (m_version.profile == Profile::ES); }
|
||||||
__fi u32 GetSurfaceWidth() const { return m_wi.surface_width; }
|
__fi u32 GetSurfaceWidth() const { return m_wi.surface_width; }
|
||||||
__fi u32 GetSurfaceHeight() const { return m_wi.surface_height; }
|
__fi u32 GetSurfaceHeight() const { return m_wi.surface_height; }
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user