diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index 9ca8d67efc..630aeed89f 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -53,9 +53,23 @@ 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! 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) { @@ -1288,6 +1302,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 +1314,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; 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();