From 60889767bfac869904558a7dfd81b0500835ce40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 25 Mar 2019 21:38:01 +0100 Subject: [PATCH 1/4] Vulkan: Request cached memory for readbacks. First try coherent too but also support non-coherent cached memory. Should speed up readbacks slightly. This is just a common sense optimization, I haven't measured it. Writes to coherent non-cached memory is OK due to hardware write combining, but for reads you really want cached to avoid a memory transaction for every single read (instead reading full cache lines). --- ext/native/thin3d/VulkanQueueRunner.cpp | 25 ++++++++++++++++++++++--- ext/native/thin3d/VulkanQueueRunner.h | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 9ca8d67efc..b290cf8ba4 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -53,9 +53,17 @@ void VulkanQueueRunner::ResizeReadbackBuffer(VkDeviceSize requiredSize) { VkMemoryAllocateInfo allocInfo{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocInfo.allocationSize = reqs.size; - VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex); - _assert_(success); + // For speedy readbacks, we want the CPU cache to be enabled. However on most hardware we then have to + // sacrifice coherency, which means manual flushing. But try to find such memory first! + VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + if (vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex)) { + readbackBufferIsCoherent_ = true; + } else { + typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex); + _assert_(success); + readbackBufferIsCoherent_ = false; + } VkResult res = vkAllocateMemory(device, &allocInfo, nullptr, &readbackMemory_); if (res != VK_SUCCESS) { @@ -1288,6 +1296,7 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT); // NOTE: Can't read the buffer using the CPU here - need to sync first. + // Doing that will also act like a heavyweight barrier ensuring that device writes are visible on the host. } void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { @@ -1299,10 +1308,20 @@ void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataForm const size_t srcPixelSize = DataFormatSizeInBytes(srcFormat); VkResult res = vkMapMemory(vulkan_->GetDevice(), readbackMemory_, 0, width * height * srcPixelSize, 0, &mappedData); + if (!readbackBufferIsCoherent_) { + VkMappedMemoryRange range{}; + range.memory = readbackMemory_; + range.offset = 0; + range.size = width * height * srcPixelSize; + vkInvalidateMappedMemoryRanges(vulkan_->GetDevice(), 1, &range); + } + if (res != VK_SUCCESS) { ELOG("CopyReadbackBuffer: vkMapMemory failed! result=%d", (int)res); return; } + + // TODO: Perform these conversions in a compute shader on the GPU. if (srcFormat == Draw::DataFormat::R8G8B8A8_UNORM) { ConvertFromRGBA8888(pixels, (const uint8_t *)mappedData, pixelStride, width, width, height, destFormat); } else if (srcFormat == Draw::DataFormat::B8G8R8A8_UNORM) { diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index cfcd4c98fc..911ef1ab0b 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -261,6 +261,7 @@ private: VkDeviceMemory readbackMemory_ = VK_NULL_HANDLE; VkBuffer readbackBuffer_ = VK_NULL_HANDLE; VkDeviceSize readbackBufferSize_ = 0; + bool readbackBufferIsCoherent_ = false; // TODO: Enable based on compat.ini. uint32_t hacksEnabled_ = 0; From 1fa7bbc816ae4f5bac7d69b0dc28aeab9c7af2f6 Mon Sep 17 00:00:00 2001 From: LunaMoo Date: Tue, 26 Mar 2019 06:12:02 +0100 Subject: [PATCH 2/4] Add texture dump/replace hotkeys. --- Common/KeyMap.cpp | 2 ++ Common/KeyMap.h | 2 ++ UI/EmuScreen.cpp | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 2a05e4e434..b50bbfce72 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -668,6 +668,8 @@ const KeyMap_IntStrPair psp_button_names[] = { {VIRTKEY_AXIS_SWAP, "AxisSwap"}, {VIRTKEY_DEVMENU, "DevMenu"}, + {VIRTKEY_TEXTURE_DUMP, "Texture Dumping"}, + {VIRTKEY_TEXTURE_REPLACE, "Texture Replacement"}, {CTRL_HOME, "Home"}, {CTRL_HOLD, "Hold"}, diff --git a/Common/KeyMap.h b/Common/KeyMap.h index a6020fc076..46e4395d69 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -54,6 +54,8 @@ enum { VIRTKEY_RECORD = 0x40000016, VIRTKEY_SPEED_CUSTOM1 = 0x40000017, VIRTKEY_SPEED_CUSTOM2 = 0x40000018, + VIRTKEY_TEXTURE_DUMP = 0x40000019, + VIRTKEY_TEXTURE_REPLACE = 0x4000001A, VIRTKEY_LAST, VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index a67624f7f8..75ce2fa089 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -606,6 +606,24 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { case VIRTKEY_TOGGLE_FULLSCREEN: System_SendMessage("toggle_fullscreen", ""); break; + + case VIRTKEY_TEXTURE_DUMP: + g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures; + if (g_Config.bSaveNewTextures) { + osm.Show(sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0); + NativeMessageReceived("gpu_clearCache", ""); + } else { + osm.Show(sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0); + } + break; + case VIRTKEY_TEXTURE_REPLACE: + g_Config.bReplaceTextures = !g_Config.bReplaceTextures; + if (g_Config.bReplaceTextures) + osm.Show(sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0); + else + osm.Show(sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); + NativeMessageReceived("gpu_clearCache", ""); + break; } } From ed1aa748271abe2f7629279407e6210e9bb975c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 26 Mar 2019 10:39:07 +0100 Subject: [PATCH 3/4] Address feedback from #11936 (support memory types without CACHED as a backup) --- ext/native/thin3d/VulkanQueueRunner.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index b290cf8ba4..630aeed89f 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -54,16 +54,22 @@ void VulkanQueueRunner::ResizeReadbackBuffer(VkDeviceSize requiredSize) { allocInfo.allocationSize = reqs.size; // For speedy readbacks, we want the CPU cache to be enabled. However on most hardware we then have to - // sacrifice coherency, which means manual flushing. But try to find such memory first! - VkFlags typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; - if (vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex)) { - readbackBufferIsCoherent_ = true; - } else { - typeReqs = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; - bool success = vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex); - _assert_(success); - readbackBufferIsCoherent_ = false; + // sacrifice coherency, which means manual flushing. But try to find such memory first! If no cached + // memory type is available we fall back to just coherent. + const VkFlags desiredTypes[] = { + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + }; + VkFlags successTypeReqs = 0; + for (VkFlags typeReqs : desiredTypes) { + if (vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, typeReqs, &allocInfo.memoryTypeIndex)) { + successTypeReqs = typeReqs; + break; + } } + _assert_(successTypeReqs != 0); + readbackBufferIsCoherent_ = (successTypeReqs & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0; VkResult res = vkAllocateMemory(device, &allocInfo, nullptr, &readbackMemory_); if (res != VK_SUCCESS) { From 53e254d352986dc1093c620c58075189fd714a65 Mon Sep 17 00:00:00 2001 From: Jeeeyul Lee Date: Wed, 27 Mar 2019 11:01:20 +0900 Subject: [PATCH 4/4] Disables iOS exit animation to prevent freezing. Selective PTRACE sys call. --- ios/ViewController.mm | 6 ++ ios/codesign.h | 65 ++++++++++++++++ ios/main.mm | 167 ++++++++++++++++++------------------------ 3 files changed, 141 insertions(+), 97 deletions(-) create mode 100644 ios/codesign.h diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 0c5c5ae5da..720963df6d 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -574,6 +574,12 @@ static GraphicsContext *graphicsContext; NativeKey(key); } +// Enables tapping for edge area. +-(UIRectEdge)preferredScreenEdgesDeferringSystemGestures +{ + return UIRectEdgeAll; +} + - (void)setupController:(GCController *)controller { self.gameController = controller; diff --git a/ios/codesign.h b/ios/codesign.h new file mode 100644 index 0000000000..57df032409 --- /dev/null +++ b/ios/codesign.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _SYS_CODESIGN_H_ +#define _SYS_CODESIGN_H_ + +#include + +/* code signing attributes of a process */ +#define CS_VALID 0x0001 /* dynamically valid */ +#define CS_HARD 0x0100 /* don't load invalid pages */ +#define CS_KILL 0x0200 /* kill process if it becomes invalid */ +#define CS_EXEC_SET_HARD 0x1000 /* set CS_HARD on any exec'ed process */ +#define CS_EXEC_SET_KILL 0x2000 /* set CS_KILL on any exec'ed process */ +#define CS_KILLED 0x10000 /* was killed by kernel for invalidity */ +#define CS_RESTRICT 0x20000 /* tell dyld to treat restricted */ + +/* csops operations */ +#define CS_OPS_STATUS 0 /* return status */ +#define CS_OPS_MARKINVALID 1 /* invalidate process */ +#define CS_OPS_MARKHARD 2 /* set HARD flag */ +#define CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */ +#define CS_OPS_PIDPATH 4 /* get executable's pathname */ +#define CS_OPS_CDHASH 5 /* get code directory hash */ +#define CS_OPS_PIDOFFSET 6 /* get offset of active Mach-o slice */ +#define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */ +#define CS_OPS_MARKRESTRICT 8 /* set RESTRICT flag (sticky) */ + +#ifndef KERNEL + +__BEGIN_DECLS + +/* code sign operations */ +int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); + +__END_DECLS + +#endif /* ! KERNEL */ + +#endif /* _SYS_CODESIGN_H_ */ diff --git a/ios/main.mm b/ios/main.mm index 7e614bce38..ce1803e023 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -6,6 +6,7 @@ #import #import #import +#import "codesign.h" #import "AppDelegate.h" #import "PPSSPPUIApplication.h" @@ -14,89 +15,53 @@ #include "base/NativeApp.h" #include "profiler/profiler.h" -@interface UIApplication (Private) --(void) suspend; --(void) terminateWithSuccess; -@end +#define CS_OPS_STATUS 0 /* return status */ +#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ +int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); -@interface UIApplication (SpringBoardAnimatedExit) --(void) animatedExit; -@end - -@implementation UIApplication (SpringBoardAnimatedExit) --(void) animatedExit { - [sharedViewController shutdown]; - - BOOL multitaskingSupported = NO; - if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) { - multitaskingSupported = [UIDevice currentDevice].multitaskingSupported; - } - if ([self respondsToSelector:@selector(suspend)]) { - if (multitaskingSupported) { - [self beginBackgroundTaskWithExpirationHandler:^{}]; - [self performSelector:@selector(exit) withObject:nil afterDelay:0.4]; - } - [self suspend]; - } else { - [self exit]; - } -} - --(void) exit { - [sharedViewController shutdown]; - - if ([self respondsToSelector:@selector(terminateWithSuccess)]) { - [self terminateWithSuccess]; - } else { - exit(0); - } -} -@end std::string System_GetProperty(SystemProperty prop) { switch (prop) { - case SYSPROP_NAME: - return "iOS:"; - case SYSPROP_LANGREGION: - return "en_US"; - default: - return ""; + case SYSPROP_NAME: + return "iOS:"; + case SYSPROP_LANGREGION: + return "en_US"; + default: + return ""; } } int System_GetPropertyInt(SystemProperty prop) { switch (prop) { - case SYSPROP_AUDIO_SAMPLE_RATE: - return 44100; - case SYSPROP_DISPLAY_REFRESH_RATE: - return 60000; - case SYSPROP_DEVICE_TYPE: - return DEVICE_TYPE_MOBILE; - default: - return -1; + case SYSPROP_AUDIO_SAMPLE_RATE: + return 44100; + case SYSPROP_DISPLAY_REFRESH_RATE: + return 60000; + case SYSPROP_DEVICE_TYPE: + return DEVICE_TYPE_MOBILE; + default: + return -1; } } bool System_GetPropertyBool(SystemProperty prop) { switch (prop) { - case SYSPROP_HAS_BACK_BUTTON: - return false; - case SYSPROP_APP_GOLD: + case SYSPROP_HAS_BACK_BUTTON: + return false; + case SYSPROP_APP_GOLD: #ifdef GOLD - return true; + return true; #else - return false; + return false; #endif - default: - return false; + default: + return false; } } void System_SendMessage(const char *command, const char *parameter) { if (!strcmp(command, "finish")) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[UIApplication sharedApplication] animatedExit]; - }); + exit(0); } } @@ -107,49 +72,57 @@ FOUNDATION_EXTERN void AudioServicesPlaySystemSoundWithVibration(unsigned long, BOOL SupportsTaptic() { - // we're on an iOS version that cannot instantiate UISelectionFeedbackGenerator, so no. - if(!NSClassFromString(@"UISelectionFeedbackGenerator")) - { - return NO; - } - - // http://www.mikitamanko.com/blog/2017/01/29/haptic-feedback-with-uifeedbackgenerator/ - // use private API against UIDevice to determine the haptic stepping - // 2 - iPhone 7 or above, full taptic feedback - // 1 - iPhone 6S, limited taptic feedback - // 0 - iPhone 6 or below, no taptic feedback - NSNumber* val = (NSNumber*)[[UIDevice currentDevice] valueForKey:@"feedbackSupportLevel"]; - return [val intValue] >= 2; + // we're on an iOS version that cannot instantiate UISelectionFeedbackGenerator, so no. + if(!NSClassFromString(@"UISelectionFeedbackGenerator")) + { + return NO; + } + + // http://www.mikitamanko.com/blog/2017/01/29/haptic-feedback-with-uifeedbackgenerator/ + // use private API against UIDevice to determine the haptic stepping + // 2 - iPhone 7 or above, full taptic feedback + // 1 - iPhone 6S, limited taptic feedback + // 0 - iPhone 6 or below, no taptic feedback + NSNumber* val = (NSNumber*)[[UIDevice currentDevice] valueForKey:@"feedbackSupportLevel"]; + return [val intValue] >= 2; } void Vibrate(int mode) { - - if(SupportsTaptic()) - { - PPSSPPUIApplication* app = (PPSSPPUIApplication*)[UIApplication sharedApplication]; - if(app.feedbackGenerator == nil) - { - app.feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init]; - [app.feedbackGenerator prepare]; - } - [app.feedbackGenerator selectionChanged]; - } - else - { - NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; - NSArray *pattern = @[@YES, @30, @NO, @2]; - - dictionary[@"VibePattern"] = pattern; - dictionary[@"Intensity"] = @2; - - AudioServicesPlaySystemSoundWithVibration(kSystemSoundID_Vibrate, nil, dictionary); - } + + if(SupportsTaptic()) + { + PPSSPPUIApplication* app = (PPSSPPUIApplication*)[UIApplication sharedApplication]; + if(app.feedbackGenerator == nil) + { + app.feedbackGenerator = [[UISelectionFeedbackGenerator alloc] init]; + [app.feedbackGenerator prepare]; + } + [app.feedbackGenerator selectionChanged]; + } + else + { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + NSArray *pattern = @[@YES, @30, @NO, @2]; + + dictionary[@"VibePattern"] = pattern; + dictionary[@"Intensity"] = @2; + + AudioServicesPlaySystemSoundWithVibration(kSystemSoundID_Vibrate, nil, dictionary); + } } int main(int argc, char *argv[]) { - // Simulates a debugger. Makes it possible to use JIT (though only W^X) - syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0); + // see https://github.com/hrydgard/ppsspp/issues/11905#issuecomment-476871010 + uint32_t flags; + csops(getpid(), CS_OPS_STATUS, &flags, 0); + if (flags & CS_DEBUGGED){ + //being run either under a debugger or under Electra already + } + else{ + // Simulates a debugger. Makes it possible to use JIT (though only W^X) + syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0); + } PROFILE_INIT();