mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 01:59:34 +01:00
iOS/tvOS: Remove Vulkan (MoltenVK), Metal only
The iOS and tvOS apps no longer link MoltenVK, and the ios/MoltenVK submodule is gone. With that, the statically linked Vulkan path (PPSSPP_VK_STATIC) has no users and is removed. VulkanMayBeAvailable() returns false in the iOS app, so a config that says Vulkan falls back to Metal, which is now the tvOS default too. macOS keeps Vulkan. The graphics settings hide the backend selector when there's only one backend to pick, using a new SettingItem::Visible() (hidden items are also left out of search results). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
80adb8baf9
commit
5ad6770e30
18 files changed
+58
-113
No files matched your search
@@ -44,9 +44,6 @@
|
||||
[submodule "ext/OpenXR-SDK"]
|
||||
path = ext/OpenXR-SDK
|
||||
url = https://github.com/KhronosGroup/OpenXR-SDK.git
|
||||
[submodule "ios/MoltenVK"]
|
||||
path = ios/MoltenVK
|
||||
url = https://github.com/hrydgard/ppsspp-moltenvk.git
|
||||
[submodule "ext/lua"]
|
||||
path = ext/lua
|
||||
url = https://github.com/hrydgard/ppsspp-lua.git
|
||||
|
||||
+1
-16
@@ -323,7 +323,7 @@ elseif(NOT OPENGL_LIBRARIES AND USING_GLES2)
|
||||
set(OPENGL_LIBRARIES GLESv2 EGL)
|
||||
endif()
|
||||
|
||||
# The iOS app is Vulkan (MoltenVK) only. Must match PPSSPP_API_ANY_GL in ppsspp_config.h.
|
||||
# The iOS app is Metal only. Must match PPSSPP_API_ANY_GL in ppsspp_config.h.
|
||||
if(IOS AND NOT LIBRETRO)
|
||||
set(NO_OPENGL ON)
|
||||
endif()
|
||||
@@ -1566,11 +1566,6 @@ if(IOS AND NOT LIBRETRO)
|
||||
if(IOS_DEBUG)
|
||||
file(INSTALL pspautotests DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets)
|
||||
endif()
|
||||
if(TVOS)
|
||||
set(MOLTENVK_SLICE tvos-arm64)
|
||||
else()
|
||||
set(MOLTENVK_SLICE ios-arm64)
|
||||
endif()
|
||||
set(RSRC_XIB_FILES "Launch Screen.storyboard" ${CMAKE_CURRENT_SOURCE_DIR}/ios/assets.xcassets)
|
||||
|
||||
set_source_files_properties(${RSRC_XIB_FILES}
|
||||
@@ -1584,15 +1579,6 @@ if(IOS AND NOT LIBRETRO)
|
||||
set(APP_DIR_NAME "$<TARGET_FILE_DIR:PPSSPP>")
|
||||
#endif()
|
||||
|
||||
# Can't figure out properly using .xcframework from CMake, so just linking directly to the .a file.
|
||||
# The simulator renders with Metal only, and loads no Vulkan (see PPSSPP_VK_STATIC).
|
||||
if(IOS_PLATFORM STREQUAL "SIMULATOR")
|
||||
set(MOLTENVK_LIB "")
|
||||
else()
|
||||
set(MOLTENVK_LIB "${CMAKE_CURRENT_SOURCE_DIR}/ios/MoltenVK/MoltenVK.xcframework/${MOLTENVK_SLICE}/libMoltenVK.a")
|
||||
endif()
|
||||
target_link_libraries(${TargetBin} ${MOLTENVK_LIB})
|
||||
|
||||
set(MACOSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET})
|
||||
set(PRODUCT_NAME "PPSSPP")
|
||||
if(TVOS)
|
||||
@@ -1697,7 +1683,6 @@ if(IOS AND NOT LIBRETRO)
|
||||
# PPSSPPHeadless as an app of its own (org.ppsspp.headless), for running frame dumps on a
|
||||
# device. Signed for development, so devicectl can copy files in and out of its container.
|
||||
target_link_libraries(PPSSPPHeadless
|
||||
${MOLTENVK_LIB}
|
||||
"-framework Foundation -framework UIKit -framework CoreGraphics -framework CoreText -framework QuartzCore -framework Metal -framework IOSurface -framework AudioToolbox -framework AVFoundation -framework CoreMedia -framework CoreVideo -framework UniformTypeIdentifiers"
|
||||
)
|
||||
if(NOT ICONV_LIBRARY)
|
||||
|
||||
@@ -251,9 +251,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
|
||||
inst_info.enabledExtensionCount = (uint32_t)instance_extensions_enabled_.size();
|
||||
inst_info.ppEnabledExtensionNames = instance_extensions_enabled_.size() ? instance_extensions_enabled_.data() : nullptr;
|
||||
|
||||
#if PPSSPP_VK_STATIC
|
||||
inst_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
#if PPSSPP_PLATFORM(MAC)
|
||||
// Through the Khronos loader (like the SDK's, for the validation layers), MoltenVK is only
|
||||
// listed if we ask for portability drivers.
|
||||
if (IsInstanceExtensionAvailable(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#endif
|
||||
|
||||
namespace PPSSPP_VK {
|
||||
#if !PPSSPP_VK_STATIC
|
||||
PFN_vkCreateInstance vkCreateInstance;
|
||||
PFN_vkDestroyInstance vkDestroyInstance;
|
||||
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
|
||||
@@ -245,14 +244,11 @@ PFN_vkCreateRenderPass2 vkCreateRenderPass2;
|
||||
PFN_vkWaitForPresentKHR vkWaitForPresentKHR;
|
||||
PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE;
|
||||
PFN_vkGetRefreshCycleDurationGOOGLE vkGetRefreshCycleDurationGOOGLE;
|
||||
#endif
|
||||
} // namespace PPSSPP_VK
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
#if PPSSPP_VK_STATIC
|
||||
// Statically linked MoltenVK
|
||||
#elif PPSSPP_PLATFORM(SWITCH)
|
||||
#if PPSSPP_PLATFORM(SWITCH)
|
||||
typedef void *VulkanLibraryHandle;
|
||||
static VulkanLibraryHandle vulkanLibrary;
|
||||
#define dlsym(x, y) nullptr
|
||||
@@ -337,10 +333,10 @@ static constexpr DriverBlackListEntry g_gpuNameBlacklist[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !PPSSPP_VK_STATIC
|
||||
#if !defined(_WIN32)
|
||||
static const char * const so_names[] = {
|
||||
#if PPSSPP_PLATFORM(IOS)
|
||||
// Only reached by the libretro core and the simulator (which has none). The app links MoltenVK statically.
|
||||
// Only reached by the libretro core. The app has no MoltenVK.
|
||||
"@executable_path/Frameworks/libMoltenVK.dylib",
|
||||
"MoltenVK",
|
||||
#elif PPSSPP_PLATFORM(MAC)
|
||||
@@ -360,7 +356,6 @@ static const char * const so_names[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !PPSSPP_VK_STATIC
|
||||
static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) {
|
||||
#if PPSSPP_PLATFORM(SWITCH)
|
||||
// Always unavailable, for now.
|
||||
@@ -418,9 +413,7 @@ static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) {
|
||||
return lib;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !PPSSPP_VK_STATIC
|
||||
static void VulkanFreeLibrary(VulkanLibraryHandle &h) {
|
||||
if (h) {
|
||||
#if PPSSPP_PLATFORM(SWITCH)
|
||||
@@ -433,7 +426,6 @@ static void VulkanFreeLibrary(VulkanLibraryHandle &h) {
|
||||
h = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void VulkanSetAvailable(bool available) {
|
||||
INFO_LOG(Log::G3D, "Setting Vulkan availability to true");
|
||||
@@ -446,7 +438,10 @@ void VulkanSetNativeLibDir(std::string_view nativeLibDir) {
|
||||
}
|
||||
|
||||
bool VulkanMayBeAvailable() {
|
||||
#if PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(IOS_SIMULATOR)
|
||||
#if PPSSPP_PLATFORM(IOS) && !defined(__LIBRETRO__)
|
||||
// The app renders with Metal, and has no MoltenVK.
|
||||
return false;
|
||||
#elif PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(IOS_SIMULATOR)
|
||||
// The deployment target (15.0) is above anything MoltenVK doesn't support.
|
||||
g_vulkanAvailabilityChecked = true;
|
||||
g_vulkanMayBeAvailable = true;
|
||||
@@ -681,11 +676,6 @@ bail:
|
||||
}
|
||||
|
||||
bool VulkanLoad(std::string *errorStr) {
|
||||
#if PPSSPP_VK_STATIC
|
||||
INFO_LOG(Log::G3D, "iOS: Vulkan doesn't need loading");
|
||||
return true;
|
||||
#else
|
||||
|
||||
if (!vulkanLibrary) {
|
||||
vulkanLibrary = VulkanLoadLibrary(errorStr);
|
||||
if (!vulkanLibrary) {
|
||||
@@ -711,15 +701,9 @@ bool VulkanLoad(std::string *errorStr) {
|
||||
VulkanFreeLibrary(vulkanLibrary);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool VulkanLoadFromGetInstanceProcAddr(VkInstance instance, PFN_vkGetInstanceProcAddr getInstanceProcAddr) {
|
||||
#if PPSSPP_VK_STATIC
|
||||
// MoltenVK is linked statically, the functions are already there - there are no function pointers to fill in.
|
||||
INFO_LOG(Log::G3D, "iOS: Vulkan doesn't need loading");
|
||||
return true;
|
||||
#else
|
||||
vkGetInstanceProcAddr = getInstanceProcAddr;
|
||||
vkCreateInstance = (PFN_vkCreateInstance)getInstanceProcAddr(nullptr, "vkCreateInstance");
|
||||
// Per the Vulkan spec, vkGetDeviceProcAddr is not one of the handful of commands queryable with a
|
||||
@@ -737,11 +721,9 @@ bool VulkanLoadFromGetInstanceProcAddr(VkInstance instance, PFN_vkGetInstancePro
|
||||
ERROR_LOG(Log::G3D, "VulkanLoadFromGetInstanceProcAddr: Failed to load Vulkan base functions");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &enabledExtensions, uint32_t vulkanInstanceApiVersion) {
|
||||
#if !PPSSPP_VK_STATIC
|
||||
INFO_LOG(Log::G3D, "Loading Vulkan instance functions. Instance API version: %08x (%d.%d.%d)", vulkanInstanceApiVersion, VK_API_VERSION_MAJOR(vulkanInstanceApiVersion), VK_API_VERSION_MINOR(vulkanInstanceApiVersion), VK_API_VERSION_PATCH(vulkanInstanceApiVersion));
|
||||
// OK, let's use the above functions to get the rest.
|
||||
LOAD_INSTANCE_FUNC(instance, vkDestroyInstance);
|
||||
@@ -810,14 +792,12 @@ void VulkanLoadInstanceFunctions(VkInstance instance, const VulkanExtensions &en
|
||||
}
|
||||
|
||||
INFO_LOG(Log::G3D, "Vulkan instance functions loaded.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// On some implementations, loading functions (that have Device as their first parameter) via vkGetDeviceProcAddr may
|
||||
// increase performance - but then these function pointers will only work on that specific device. Thus, this loader is not very
|
||||
// good for multi-device - not likely we'll ever try that anyway though.
|
||||
void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledExtensions, uint32_t vulkanDeviceApiVersion) {
|
||||
#if !PPSSPP_VK_STATIC
|
||||
INFO_LOG(Log::G3D, "Loading Vulkan device functions. Device API version: %08x (%d.%d.%d)", vulkanDeviceApiVersion, VK_API_VERSION_MAJOR(vulkanDeviceApiVersion), VK_API_VERSION_MINOR(vulkanDeviceApiVersion), VK_API_VERSION_PATCH(vulkanDeviceApiVersion));
|
||||
|
||||
LOAD_DEVICE_FUNC(device, vkQueueSubmit);
|
||||
@@ -958,13 +938,10 @@ void VulkanLoadDeviceFunctions(VkDevice device, const VulkanExtensions &enabledE
|
||||
LOAD_DEVICE_FUNC_CORE(device, vkGetDeviceBufferMemoryRequirements, vkGetDeviceBufferMemoryRequirementsKHR, VK_API_VERSION_1_3);
|
||||
LOAD_DEVICE_FUNC_CORE(device, vkGetDeviceImageMemoryRequirements, vkGetDeviceImageMemoryRequirementsKHR, VK_API_VERSION_1_3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VulkanFree() {
|
||||
#if !PPSSPP_VK_STATIC
|
||||
VulkanFreeLibrary(vulkanLibrary);
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *VulkanResultToString(VkResult res) {
|
||||
|
||||
@@ -32,14 +32,9 @@
|
||||
#define VK_USE_PLATFORM_METAL_EXT
|
||||
#endif
|
||||
|
||||
// The iOS app links MoltenVK statically and calls Vulkan directly. Everywhere else (including the
|
||||
// iOS libretro core), we load the functions at runtime into the pointers below. The simulator build
|
||||
// has no MoltenVK at all (it uses Metal), so it takes the runtime path, which finds no library.
|
||||
#if PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(IOS_SIMULATOR) && !defined(__LIBRETRO__)
|
||||
#define PPSSPP_VK_STATIC 1
|
||||
#else
|
||||
// We load the functions at runtime into the pointers below. The iOS app has no MoltenVK (it uses
|
||||
// Metal), so there this finds no library.
|
||||
#define VK_NO_PROTOTYPES
|
||||
#endif
|
||||
#define VK_ENABLE_BETA_EXTENSIONS 1 // VK_KHR_portability_subset
|
||||
|
||||
#include "ext/vulkan/vulkan.h"
|
||||
@@ -51,7 +46,6 @@
|
||||
#endif
|
||||
|
||||
namespace PPSSPP_VK {
|
||||
#if !PPSSPP_VK_STATIC
|
||||
// Putting our own Vulkan function pointers in a namespace ensures that ppsspp_libretro.so doesn't collide with libvulkan.so.
|
||||
extern PFN_vkCreateInstance vkCreateInstance;
|
||||
extern PFN_vkDestroyInstance vkDestroyInstance;
|
||||
@@ -252,7 +246,6 @@ extern PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesE
|
||||
extern PFN_vkWaitForPresentKHR vkWaitForPresentKHR;
|
||||
extern PFN_vkGetPastPresentationTimingGOOGLE vkGetPastPresentationTimingGOOGLE;
|
||||
extern PFN_vkGetRefreshCycleDurationGOOGLE vkGetRefreshCycleDurationGOOGLE;
|
||||
#endif // !PPSSPP_VK_STATIC
|
||||
} // namespace PPSSPP_VK
|
||||
|
||||
// For fast extension-enabled checks.
|
||||
|
||||
@@ -900,7 +900,6 @@ void VulkanRenderManager::RenderThreadFunc() {
|
||||
void VulkanRenderManager::PresentWaitThreadFunc() {
|
||||
SetCurrentThreadName("PresentWait");
|
||||
|
||||
#if !PPSSPP_VK_STATIC
|
||||
_dbg_assert_(vkWaitForPresentKHR != nullptr);
|
||||
|
||||
uint64_t waitedId = frameIdGen_;
|
||||
@@ -917,7 +916,6 @@ void VulkanRenderManager::PresentWaitThreadFunc() {
|
||||
}
|
||||
_dbg_assert_(waitedId <= frameIdGen_);
|
||||
}
|
||||
#endif
|
||||
|
||||
INFO_LOG(Log::G3D, "Leaving PresentWaitThreadFunc()");
|
||||
}
|
||||
|
||||
@@ -661,9 +661,6 @@ static int DefaultGPUBackend() {
|
||||
return (int)GPUBackend::OPENGL;
|
||||
#endif
|
||||
|
||||
#elif PPSSPP_PLATFORM(TVOS) && !defined(__LIBRETRO__)
|
||||
// Metal hasn't been tried on tvOS yet.
|
||||
return (int)GPUBackend::VULKAN;
|
||||
#elif PPSSPP_PLATFORM(IOS) && !defined(__LIBRETRO__)
|
||||
return (int)GPUBackend::METAL;
|
||||
#endif
|
||||
|
||||
@@ -139,6 +139,17 @@ static void BuildCardboardVRPage(SettingsPage *page) {
|
||||
page->Setting(&config.iCardboardYShift).Enabled(&config.bEnableCardboardVR);
|
||||
}
|
||||
|
||||
// Asking about Vulkan can load its driver, so that's done last, and only when it decides the answer.
|
||||
static bool HasBackendChoice() {
|
||||
int count = 0;
|
||||
for (GPUBackend backend : { GPUBackend::OPENGL, GPUBackend::DIRECT3D11, GPUBackend::METAL }) {
|
||||
if (g_Config.IsBackendEnabled(backend)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count >= 2 || (count == 1 && g_Config.IsBackendEnabled(GPUBackend::VULKAN));
|
||||
}
|
||||
|
||||
void BuildGraphicsSettings(SettingsPage *page) {
|
||||
using namespace UI;
|
||||
SettingsPageHost *host = page->Host();
|
||||
@@ -149,7 +160,7 @@ void BuildGraphicsSettings(SettingsPage *page) {
|
||||
page->Header("Rendering");
|
||||
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
page->Setting(&g_Config.iGPUBackend).Setup([host](View *view) {
|
||||
page->Setting(&g_Config.iGPUBackend).Visible(HasBackendChoice()).Setup([host](View *view) {
|
||||
PopupMultiChoice *renderingBackendChoice = static_cast<PopupMultiChoice *>(view);
|
||||
renderingBackendChoice->SetPreOpenCallback([host](UI::PopupMultiChoice *choice) {
|
||||
// Don't filter until the last possible moment, since it involves trying to initialize Vulkan, if we were
|
||||
|
||||
+7
-1
@@ -430,12 +430,18 @@ static UI::View *CreateSettingView(const SettingItem &item, I18NCat pageCat, UI:
|
||||
clickable->SetHintText(item.hint.Translate(pageCat));
|
||||
} else {
|
||||
// Custom views that can't show a hint get it below them.
|
||||
parent->Add(new SettingHint(item.hint.Translate(pageCat), view));
|
||||
UI::View *hint = parent->Add(new SettingHint(item.hint.Translate(pageCat), view));
|
||||
if (!item.visible) {
|
||||
hint->SetVisibility(UI::V_GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.setup) {
|
||||
item.setup(view);
|
||||
}
|
||||
if (!item.visible) {
|
||||
view->SetVisibility(UI::V_GONE);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,8 @@ struct SettingItem {
|
||||
bool *disabledPtr = nullptr;
|
||||
std::function<bool()> enabledFunc;
|
||||
SettingText disabledReason;
|
||||
// A hidden item still gets its view (it's gone from the layout), so views stay in step with items.
|
||||
bool visible = true;
|
||||
// Called on click (Bool, Choice), choice (choices) or change (sliders).
|
||||
std::function<void(UI::EventParams &)> onChange;
|
||||
// From the config, for settings described there. Called before onChange.
|
||||
@@ -168,6 +170,7 @@ struct SettingItem {
|
||||
SettingItem &Enabled(bool *ptr) { enabledPtr = ptr; return *this; }
|
||||
SettingItem &Disabled(bool *ptr) { disabledPtr = ptr; return *this; }
|
||||
SettingItem &EnabledFunc(std::function<bool()> func) { enabledFunc = std::move(func); return *this; }
|
||||
SettingItem &Visible(bool value) { visible = value; return *this; }
|
||||
// Shown when the user tries to use the item while it's disabled (see ShowWhyViewDisabled).
|
||||
SettingItem &DisabledReason(SettingText text) { disabledReason = text; return *this; }
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ bool SettingsPagesDialogScreen::SearchPage(UI::ViewGroup *parent, int settingsTa
|
||||
if (!item.hint.empty() && SettingTextMatches(item.hint, cat, searchText_)) {
|
||||
matches = true;
|
||||
}
|
||||
show[i] = matches || sectionMatches;
|
||||
show[i] = item.visible && (matches || sectionMatches);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# The Metal backend
|
||||
|
||||
A native Metal backend for Mac and iOS (tvOS too), alongside OpenGL, D3D11 and Vulkan. It's selected as
|
||||
`GPUBackend::METAL` (`--graphics=metal`). It's the default on iOS since 2026-10-22; Vulkan through MoltenVK
|
||||
stays the default on Mac and tvOS. Written 2026-10-19; file references will drift.
|
||||
`GPUBackend::METAL` (`--graphics=metal`). It's the only backend in the iOS and tvOS apps, which no longer ship
|
||||
MoltenVK; Vulkan through MoltenVK stays the default on Mac. Written 2026-10-19; file references will drift.
|
||||
|
||||
**Why:** programmable blending. Apple GPUs let a fragment shader read the color it's about to blend with
|
||||
(`[[color(0)]]`), so PSP blend modes that need shader blending no longer copy the framebuffer for every
|
||||
draw. MoltenVK can't expose that. Removing the MoltenVK dependency is a smaller benefit, and comes later.
|
||||
draw. MoltenVK can't expose that.
|
||||
|
||||
## Where things are
|
||||
|
||||
|
||||
@@ -117,11 +117,6 @@ static bool g_launched = false;
|
||||
return;
|
||||
}
|
||||
// Without a window, iOS mirrors the phone.
|
||||
// The running backend: the setting is already the new one while the restart prompt for a change is up.
|
||||
if (GetGPUBackend() != GPUBackend::METAL && GetGPUBackend() != GPUBackend::VULKAN) {
|
||||
INFO_LOG(Log::G3D, "External display: mirroring, as the backend can't present to it");
|
||||
return;
|
||||
}
|
||||
if (g_Config.iExternalDisplay != (int)ExternalDisplayMode::SHOW_GAME) {
|
||||
INFO_LOG(Log::G3D, "External display: mirroring, as set");
|
||||
return;
|
||||
|
||||
-1
Submodule ios/MoltenVK deleted from b77380b5c8.
@@ -1,10 +1,3 @@
|
||||
Updating with a self-built MoltenVK
|
||||
===================================
|
||||
|
||||
Probably won't be needed again.
|
||||
|
||||
cp -r ../dev/build-molten/MoltenVK/Package/Release/MoltenVK/static/MoltenVK.xcframework ios/MoltenVK
|
||||
|
||||
The Old iOS Build Instructions
|
||||
==============================
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#if PPSSPP_PLATFORM(TVOS)
|
||||
|
||||
// The one view controller. Renders with Vulkan (MoltenVK) to a CAMetalLayer.
|
||||
// The one view controller. Renders with Metal to a CAMetalLayer.
|
||||
// A GCEventViewController, so controller input (the Siri Remote included) reaches us only through
|
||||
// GCController, instead of also driving UIKit's focus engine.
|
||||
@interface PPSSPPViewController : GCEventViewController
|
||||
@@ -20,7 +20,7 @@
|
||||
#import "CameraHelper.h"
|
||||
#import "LocationHelper.h"
|
||||
|
||||
// The one view controller. Renders with Vulkan (MoltenVK) to a CAMetalLayer.
|
||||
// The one view controller. Renders with Metal to a CAMetalLayer.
|
||||
@interface PPSSPPViewController : UIViewController<
|
||||
PHPickerViewControllerDelegate,
|
||||
CameraFrameDelegate, LocationHandlerDelegate,
|
||||
|
||||
+18
-25
@@ -13,7 +13,6 @@
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include "Common/GPU/GraphicsContext.h"
|
||||
#include "Common/GPU/Vulkan/VulkanGraphicsContext.h"
|
||||
#include "Common/GPU/Metal/MetalGraphicsContext.h"
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/System/System.h"
|
||||
@@ -108,7 +107,7 @@ static void ApplyPendingResolution() {
|
||||
}
|
||||
}
|
||||
|
||||
static void VulkanRenderLoop(GraphicsContext *graphicsContext, CAMetalLayer *metalLayer) {
|
||||
static void RenderLoop(GraphicsContext *graphicsContext, CAMetalLayer *metalLayer) {
|
||||
// Only a provisional name, RunGraphicsLoop renames the thread once it knows its role.
|
||||
SetCurrentThreadName("RenderLoop");
|
||||
_assert_(graphicsContext);
|
||||
@@ -239,10 +238,10 @@ static int GetPickerRequestId(id picker) {
|
||||
_dbg_assert_(sharedViewController != nil);
|
||||
sharedViewController = nil;
|
||||
|
||||
g_Config.Save("shutdown vk");
|
||||
g_Config.Save("shutdown");
|
||||
|
||||
// Normally already done by willResignActive, but not when the app is terminated.
|
||||
[self requestExitVulkanRenderLoop];
|
||||
[self requestExitRenderLoop];
|
||||
|
||||
if (graphicsContext) {
|
||||
graphicsContext->ShutdownAPI();
|
||||
@@ -252,24 +251,24 @@ static int GetPickerRequestId(id picker) {
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
INFO_LOG(Log::System, "dealloc VK");
|
||||
INFO_LOG(Log::System, "dealloc");
|
||||
}
|
||||
|
||||
- (bool)runVulkanRenderLoop {
|
||||
INFO_LOG(Log::G3D, "runVulkanRenderLoop");
|
||||
- (bool)runRenderLoop {
|
||||
INFO_LOG(Log::G3D, "runRenderLoop");
|
||||
|
||||
if (!graphicsContext) {
|
||||
ERROR_LOG(Log::G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context.");
|
||||
ERROR_LOG(Log::G3D, "runRenderLoop: Tried to enter without a created graphics context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_renderLoopThread.joinable()) {
|
||||
if (renderLoopRunning) {
|
||||
ERROR_LOG(Log::G3D, "runVulkanRenderLoop: Already running");
|
||||
ERROR_LOG(Log::G3D, "runRenderLoop: Already running");
|
||||
return false;
|
||||
}
|
||||
// The previous thread gave up by itself (failed surface init). Reap it so we can try again.
|
||||
WARN_LOG(Log::G3D, "runVulkanRenderLoop: Joining a render thread that had already exited");
|
||||
WARN_LOG(Log::G3D, "runRenderLoop: Joining a render thread that had already exited");
|
||||
g_renderLoopThread.join();
|
||||
g_renderLoopThread = std::thread();
|
||||
}
|
||||
@@ -279,12 +278,12 @@ static int GetPickerRequestId(id picker) {
|
||||
CAMetalLayer *metalLayer = (CAMetalLayer *)self.view.layer;
|
||||
// Set before the thread exists, so an exit request can't slip in ahead of it.
|
||||
renderLoopRunning = true;
|
||||
g_renderLoopThread = std::thread(VulkanRenderLoop, graphicsContext, metalLayer);
|
||||
g_renderLoopThread = std::thread(RenderLoop, graphicsContext, metalLayer);
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)requestExitVulkanRenderLoop {
|
||||
INFO_LOG(Log::G3D, "requestExitVulkanRenderLoop");
|
||||
- (void)requestExitRenderLoop {
|
||||
INFO_LOG(Log::G3D, "requestExitRenderLoop");
|
||||
|
||||
// Don't go by renderLoopRunning - the thread might have bailed by itself, and still needs joining.
|
||||
if (!g_renderLoopThread.joinable()) {
|
||||
@@ -343,11 +342,11 @@ static int GetPickerRequestId(id picker) {
|
||||
|
||||
// Spin up the render thread, which in turn starts the emu thread.
|
||||
[self updateResolutionWithView:self.view];
|
||||
[self runVulkanRenderLoop];
|
||||
[self runRenderLoop];
|
||||
}
|
||||
|
||||
- (void)willResignActive {
|
||||
[self requestExitVulkanRenderLoop];
|
||||
[self requestExitRenderLoop];
|
||||
|
||||
#if !PPSSPP_PLATFORM(TVOS)
|
||||
g_touchTracker.CancelAll();
|
||||
@@ -631,11 +630,7 @@ static int GetPickerRequestId(id picker) {
|
||||
keyboardInputView_ = [[KeyboardInputView alloc] initWithFrame:CGRectZero];
|
||||
[self.view addSubview:keyboardInputView_];
|
||||
|
||||
if (g_Config.iGPUBackend == (int)GPUBackend::METAL) {
|
||||
graphicsContext = new MetalGraphicsContext();
|
||||
} else {
|
||||
graphicsContext = new VulkanGraphicsContext();
|
||||
}
|
||||
graphicsContext = new MetalGraphicsContext();
|
||||
std::string errorMessage;
|
||||
if (!graphicsContext->InitAPI(nullptr, &g_Config.sVulkanDevice, &errorMessage)) {
|
||||
delete graphicsContext;
|
||||
@@ -650,11 +645,9 @@ static int GetPickerRequestId(id picker) {
|
||||
// From iOS 27, an external display scene only goes to apps that register for one like this. Earlier
|
||||
// versions go by the Info.plist role instead. Without the scene, iOS mirrors the phone.
|
||||
if (@available(iOS 27.0, *)) {
|
||||
if (g_Config.iGPUBackend == (int)GPUBackend::METAL || g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
|
||||
UISceneConfiguration *sceneConfiguration = [UISceneConfiguration new];
|
||||
sceneConfiguration.delegateClass = [ExternalDisplaySceneDelegate class];
|
||||
self.externalDisplayRegistration = [self registerSceneAccessory:[UISceneAccessory externalNonInteractiveSceneAccessoryWithConfiguration:sceneConfiguration]];
|
||||
}
|
||||
UISceneConfiguration *sceneConfiguration = [UISceneConfiguration new];
|
||||
sceneConfiguration.delegateClass = [ExternalDisplaySceneDelegate class];
|
||||
self.externalDisplayRegistration = [self registerSceneAccessory:[UISceneAccessory externalNonInteractiveSceneAccessoryWithConfiguration:sceneConfiguration]];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
// main.mm: JIT enablement, the System_ functions, starting the next step
|
||||
// AppDelegate.mm: Audio session interruptions, deep link and library export helpers
|
||||
// SceneDelegate.mm: Runs NativeInit, creates the window and the view controller
|
||||
// ViewController.mm: The view controller. Rendering (Vulkan to a CAMetalLayer), input, keyboard, pickers and sizing
|
||||
// ViewController.mm: The view controller. Rendering (Metal to a CAMetalLayer), input, keyboard, pickers and sizing
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <dlfcn.h>
|
||||
|
||||
Reference in new issue
Block a user