diff --git a/apple/OSX/platform.m b/apple/OSX/platform.m index 1b63533b75..43178cdbfa 100644 --- a/apple/OSX/platform.m +++ b/apple/OSX/platform.m @@ -185,14 +185,48 @@ static char** waiting_argv; waiting_argc = 0; } +- (void) poll_iteration +{ + NSEvent *event; + + do + { + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + + [NSApp sendEvent: event]; + }while(event != nil); +} + +- (void) do_iteration +{ + int ret = 0; + while (ret != -1) + { + [self poll_iteration]; + ret = rarch_main_iterate(); + while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, FALSE) == kCFRunLoopRunHandledSource); + } + + main_exit(NULL); +} + +- (void) apple_start_iteration +{ + [self performSelectorOnMainThread:@selector(do_iteration) withObject:nil waitUntilDone:NO]; +} + +- (void) apple_stop_iteration +{ +} + - (void)applicationDidBecomeActive:(NSNotification *)notification { - apple_start_iteration(); + [self apple_start_iteration]; } - (void)applicationWillResignActive:(NSNotification *)notification { - apple_stop_iteration(); + [self apple_stop_iteration]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication diff --git a/apple/common/RAGameView.m b/apple/common/RAGameView.m index e5b6ace88c..3c8d3f9c83 100644 --- a/apple/common/RAGameView.m +++ b/apple/common/RAGameView.m @@ -17,6 +17,8 @@ #import #import "RetroArch_Apple.h" #include "../../general.h" +#include "../../runloop.h" +#include "../../frontend/frontend.h" /* Define compatibility symbols and categories. */ @@ -193,3 +195,4 @@ static void apple_gfx_ctx_update(void); #ifdef HAVE_LOCATION #include "apple_location.c.inl" #endif + diff --git a/apple/iOS/platform.m b/apple/iOS/platform.m index 460eb697a2..1167dd1fc9 100644 --- a/apple/iOS/platform.m +++ b/apple/iOS/platform.m @@ -265,7 +265,44 @@ void notify_content_loaded(void) { apple_gamecontroller_init(); - apple_start_iteration(); + [self apple_start_iteration]; +} + +#if 0 +- (void) poll_iteration +{ + UIEvent *event; + + do + { + event = [UIApplication nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; + + + [UIApplication sendEvent: event]; + }while(event != nil); +} +#endif + +- (void) do_iteration +{ + int ret = 0; + while (ret != -1) + { + //[self poll_iteration]; + ret = rarch_main_iterate(); + while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, FALSE) == kCFRunLoopRunHandledSource); + } + + main_exit(NULL); +} + +- (void) apple_start_iteration +{ + [self performSelectorOnMainThread:@selector(do_iteration) withObject:nil waitUntilDone:NO]; +} + +- (void) apple_stop_iteration +{ } - (void)applicationDidBecomeActive:(UIApplication *)application diff --git a/frontend/drivers/platform_apple.c b/frontend/drivers/platform_apple.c index 802179e8be..21dffdf532 100644 --- a/frontend/drivers/platform_apple.c +++ b/frontend/drivers/platform_apple.c @@ -28,147 +28,6 @@ #include #include -//#define DEBUG_RUNLOOP - -#ifdef DEBUG_RUNLOOP -#include "../../retroarch_logger.h" - -#define CF_LOG(...) RARCH_LOG(__VA_ARGS__) -#else -#define CF_LOG(...) -#endif - -void apple_start_iteration(void); - -void apple_stop_iteration(void); - -static CFRunLoopObserverRef iterate_observer = NULL; - -static int do_ra_iteration(void) -{ - int ret = rarch_main_iterate(); - if (ret == -1) - main_exit(NULL); - - return ret; -} - -static void do_iteration(CFRunLoopObserverRef observer, CFRunLoopActivity activity, - void *info) -{ - switch (activity) - { - case kCFRunLoopEntry: - CF_LOG("RUNLOOP ENTRY, frame: %d.\n", g_runloop.frames.video.count); - break; - case kCFRunLoopBeforeTimers: - CF_LOG("RUNLOOP BEFORE TIMERS, frame: %d.\n", g_runloop.frames.video.count); - break; - case kCFRunLoopAfterWaiting: - CF_LOG("RUNLOOP AFTER WAITING, frame: %d.\n", g_runloop.frames.video.count); - break; - case kCFRunLoopBeforeWaiting: - { - CF_LOG("RUNLOOP BEFORE WAITING, frame: %d.\n", g_runloop.frames.video.count); - int ret = do_ra_iteration(); - if (ret == -1) - return; - - CFRunLoopWakeUp(CFRunLoopGetMain()); - - /* TODO/FIXME - I am almost positive that this is not necessary and is actually a - bad thing. - - 1st. Why it is bad thing. - - This wakes up the main event loop immediately and the main loop - has only one observer, which is this function. In other words, - this causes the function to be called immediately. I did an - experiment where I saved the time before calling this and then - reported the difference between it and the start of - do_iteration, and as expected it was about 0. As a result, when - we remove this, idle performance (i.e. displaying the RetroArch - menu) is 0% CPU as desired. - - 2nd. Why it is not necessary. - - The event loop will wake up itself when there is input to the - process. This includes touch events, keyboard, bluetooth, - etc. Thus, it will be woken up and without any intervention so - that we can process that event. - - Nota bene. Why this analysis might be wrong (and what to do about it). - - If RA is not idle and is running a core, then I believe it is - designed to expect to be called in a busy loop like this because - it implements its own frame timer to ensure that the emulation - simulation isn't too fast. In that case, this change would only - allow emulation to run when there was input, which would make - all games turn-based. :) - - There are two good ways to fix this and still have the desired - 0% CPU idle behavior. - - Approach 1: Change main_entry_decide from returning a boolean - (two-values) that are interpreted as CONTINUE and QUIT. Into - returning a char-sized enum with three values that are - interpreted as QUIT, WAIT, and AGAIN, such that QUIT calls - main_exit, WAIT doesn't wake up the loop, and AGAIN does. It - would then return AGAIN when a core was active. An ugly way to - get the same effect is to look have this code just look at - g_runloop.is_menu and use the WAIT behavior in that case. - - Approach 2: Instead of signalling outside of RA whether a core - is running, instead externalize the frame time that is inside - retroarch. change main_entry_decide to return a value in - [-1,MAX_INT] where -1 is interpreted as QUIT, [0,MAX_INT) is - interpreted as the amount of time to wait until continuing, and - MAX_INT is interpreted as WAIT. This could be more robust - because we'd be exposing the scheduling behavior of RA to iOS, - which might be good in other platforms as well. - - Approach 1 is the simplest and essentially just pushes down - these requirements to rarch_main_iterate. I have gone with the - "ugly way" first because it is the most expedient and - safe. Other eyeballs should decide if it isn't necessary. - */ - } - break; - case kCFRunLoopBeforeSources: - CF_LOG("RUNLOOP BEFORE SOURCES, frame: %d\n.", g_runloop.frames.video.count); - break; - case kCFRunLoopExit: - CF_LOG("RUNLOOP EXIT, frame: %d.\n", g_runloop.frames.video.count); - break; - case kCFRunLoopAllActivities: - CF_LOG("RUNLOOP ALL ACTIVITIES, frame: %d.\n", g_runloop.frames.video.count); - break; - } -} - -void apple_start_iteration(void) -{ - if (iterate_observer) - return; - - iterate_observer = - CFRunLoopObserverCreate(0, kCFRunLoopBeforeWaiting, - true, 0, (CFRunLoopObserverCallBack)do_iteration, 0); - CFRunLoopAddObserver(CFRunLoopGetMain(), iterate_observer, - kCFRunLoopCommonModes); -} - -void apple_stop_iteration(void) -{ - if (!iterate_observer) - return; - - CFRunLoopObserverInvalidate(iterate_observer); - CFRelease(iterate_observer); - iterate_observer = NULL; -} - static void frontend_apple_get_environment_settings(int *argc, char *argv[], void *args, void *params_data) {