summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjdgleaver2020-10-23 15:22:56 +0100
committerjdgleaver2020-10-23 15:37:48 +0100
commit00406feed91e4a6b2b398093fee937ee5f5bdfdc (patch)
treee7eb952b728f800a9336ffb99442609df2d223de
parente5bb2ffdd21ef83baf15152969e36dcf23bf80f1 (diff)
downloadpicogpsp-00406feed91e4a6b2b398093fee937ee5f5bdfdc.tar.gz
picogpsp-00406feed91e4a6b2b398093fee937ee5f5bdfdc.tar.bz2
picogpsp-00406feed91e4a6b2b398093fee937ee5f5bdfdc.zip
Add build-time option to run the emulator in a thread instead of libco (fixes OpenDingux target)
-rw-r--r--Makefile11
-rw-r--r--Makefile.common6
-rw-r--r--libretro.c39
-rw-r--r--retro_emu_thread.c175
-rw-r--r--retro_emu_thread.h51
5 files changed, 279 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index dd5454b..62554cb 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ FRONTEND_SUPPORTS_RGB565=1
FORCE_32BIT_ARCH=0
HAVE_MMAP=0
HAVE_MMAP_WIN32=0
+USE_LIBCO=1
UNAME=$(shell uname -a)
@@ -405,8 +406,8 @@ else ifeq ($(platform), gcw0)
AR ?= /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar
SHARED := -shared -nostdlib -Wl,--version-script=link.T
fpic := -fPIC
- CFLAGS += $(PTHREAD_FLAGS) -DHAVE_MKDIR
- CFLAGS += -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
+ CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float
+ USE_LIBCO = 0
# Windows
else
@@ -445,6 +446,12 @@ OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_ASM:.S=.o)
DEFINES := -DHAVE_STRINGS_H -DHAVE_STDINT_H -DHAVE_INTTYPES_H -D__LIBRETRO__ -DINLINE=inline -Wall
+ifeq ($(USE_LIBCO), 1)
+DEFINES += -DUSE_LIBCO
+else
+LDFLAGS += -lpthread
+endif
+
ifeq ($(HAVE_DYNAREC), 1)
DEFINES += -DHAVE_DYNAREC
endif
diff --git a/Makefile.common b/Makefile.common
index 92fd9fa..ea75b78 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -13,9 +13,13 @@ SOURCES_C := $(CORE_DIR)/main.c \
$(CORE_DIR)/sound.c \
$(CORE_DIR)/cheats.c \
$(CORE_DIR)/libretro.c \
- $(CORE_DIR)/libco/libco.c \
$(CORE_DIR)/gba_cc_lut.c
+ifeq ($(USE_LIBCO), 1)
+SOURCES_C += $(CORE_DIR)/libco/libco.c
+else
+SOURCES_C += $(CORE_DIR)/retro_emu_thread.c
+endif
ifeq ($(HAVE_DYNAREC), 1)
SOURCES_C += $(CORE_DIR)/cpu_threaded.c
diff --git a/libretro.c b/libretro.c
index 6790982..51c40e7 100644
--- a/libretro.c
+++ b/libretro.c
@@ -5,6 +5,7 @@
#include <stdint.h>
#include "common.h"
#include "libco.h"
+#include "retro_emu_thread.h"
#include "libretro.h"
#include "libretro_core_options.h"
#include "memmap.h"
@@ -73,8 +74,10 @@ static retro_environment_t environ_cb;
struct retro_perf_callback perf_cb;
+#if defined(USE_LIBCO)
static cothread_t main_thread;
static cothread_t cpu_thread;
+#endif
int dynarec_enable;
int use_libretro_save_method = 0;
@@ -92,14 +95,23 @@ static bool post_process_mix = false;
void switch_to_main_thread(void)
{
+#if defined(USE_LIBCO)
co_switch(main_thread);
+#else
+ retro_switch_thread();
+#endif
}
static inline void switch_to_cpu_thread(void)
{
+#if defined(USE_LIBCO)
co_switch(cpu_thread);
+#else
+ retro_switch_thread();
+#endif
}
+#if defined(USE_LIBCO)
static void cpu_thread_entry(void)
{
#ifdef HAVE_DYNAREC
@@ -108,16 +120,29 @@ static void cpu_thread_entry(void)
#endif
execute_arm(execute_cycles);
}
+#endif
static inline void init_context_switch(void)
{
+#if defined(USE_LIBCO)
main_thread = co_active();
cpu_thread = co_create(0x20000, cpu_thread_entry);
+#else
+ if (!retro_init_emu_thread(dynarec_enable, execute_cycles))
+ if (log_cb)
+ log_cb(RETRO_LOG_ERROR, "[gpSP]: Failed to initialize emulation thread!\n");
+#endif
}
static inline void deinit_context_switch(void)
{
+#if defined(USE_LIBCO)
co_delete(cpu_thread);
+#else
+ retro_cancel_emu_thread();
+ retro_join_emu_thread();
+ retro_deinit_emu_thread();
+#endif
}
#if defined(PSP)
@@ -933,6 +958,20 @@ void retro_run(void)
{
bool updated = false;
+#if !defined(USE_LIBCO)
+ if (!retro_is_emu_thread_initialized())
+ {
+ environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
+ return;
+ }
+ if (retro_emu_thread_exited())
+ {
+ environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
+ retro_join_emu_thread();
+ return;
+ }
+#endif
+
update_input();
input_poll_cb();
diff --git a/retro_emu_thread.c b/retro_emu_thread.c
new file mode 100644
index 0000000..2df7db7
--- /dev/null
+++ b/retro_emu_thread.c
@@ -0,0 +1,175 @@
+// This is copyrighted software. More information is at the end of this file.
+#include "retro_emu_thread.h"
+
+#include <pthread.h>
+
+static pthread_t main_thread;
+static pthread_t emu_thread;
+static pthread_mutex_t emu_mutex;
+static pthread_mutex_t main_mutex;
+static pthread_cond_t emu_cv;
+static pthread_cond_t main_cv;
+static bool emu_keep_waiting = true;
+static bool main_keep_waiting = true;
+static bool emu_has_exited = false;
+static bool emu_thread_canceled = false;
+static bool emu_thread_initialized = false;
+
+static void* retro_run_emulator(void *args)
+{
+ char *args_str = (char *)args;
+ bool dynarec = (*args_str++ == 1) ? true : false;
+ u32 cycles = strtol(args_str, NULL, 10);
+
+ emu_has_exited = false;
+ emu_thread_canceled = false;
+
+#if defined(HAVE_DYNAREC)
+ if (dynarec)
+ execute_arm_translate(cycles);
+#endif
+ execute_arm(cycles);
+
+ emu_has_exited = true;
+ return NULL;
+}
+
+static void retro_switch_to_emu_thread()
+{
+ pthread_mutex_lock(&emu_mutex);
+ emu_keep_waiting = false;
+ pthread_mutex_unlock(&emu_mutex);
+ pthread_mutex_lock(&main_mutex);
+ pthread_cond_signal(&emu_cv);
+
+ main_keep_waiting = true;
+ while (main_keep_waiting)
+ {
+ pthread_cond_wait(&main_cv, &main_mutex);
+ }
+ pthread_mutex_unlock(&main_mutex);
+}
+
+static void retro_switch_to_main_thread()
+{
+ pthread_mutex_lock(&main_mutex);
+ main_keep_waiting = false;
+ pthread_mutex_unlock(&main_mutex);
+ pthread_mutex_lock(&emu_mutex);
+ pthread_cond_signal(&main_cv);
+
+ emu_keep_waiting = true;
+ while (emu_keep_waiting)
+ {
+ pthread_cond_wait(&emu_cv, &emu_mutex);
+ }
+ pthread_mutex_unlock(&emu_mutex);
+}
+
+void retro_switch_thread()
+{
+ if (pthread_self() == main_thread)
+ retro_switch_to_emu_thread();
+ else
+ retro_switch_to_main_thread();
+}
+
+bool retro_init_emu_thread(bool dynarec, u32 cycles)
+{
+ char args[256];
+ args[0] = '\0';
+
+ if (emu_thread_initialized)
+ return true;
+
+ /* Keep this very simple:
+ * - First character: dynarec, 0/1
+ * - Remaining characters: cycles */
+ snprintf(args, sizeof(args), " %u", cycles);
+ args[0] = dynarec ? 1 : 0;
+
+ main_thread = pthread_self();
+ if (pthread_mutex_init(&main_mutex, NULL))
+ goto main_mutex_error;
+ if (pthread_mutex_init(&emu_mutex, NULL))
+ goto emu_mutex_error;
+ if (pthread_cond_init(&main_cv, NULL))
+ goto main_cv_error;
+ if (pthread_cond_init(&emu_cv, NULL))
+ goto emu_cv_error;
+ if (pthread_create(&emu_thread, NULL, retro_run_emulator, args))
+ goto emu_thread_error;
+
+ emu_thread_initialized = true;
+ return true;
+
+emu_thread_error:
+ pthread_cond_destroy(&emu_cv);
+emu_cv_error:
+ pthread_cond_destroy(&main_cv);
+main_cv_error:
+ pthread_mutex_destroy(&emu_mutex);
+emu_mutex_error:
+ pthread_mutex_destroy(&main_mutex);
+main_mutex_error:
+ return false;
+}
+
+void retro_deinit_emu_thread()
+{
+ if (!emu_thread_initialized)
+ return;
+
+ pthread_mutex_destroy(&main_mutex);
+ pthread_mutex_destroy(&emu_mutex);
+ pthread_cond_destroy(&main_cv);
+ pthread_cond_destroy(&emu_cv);
+ emu_thread_initialized = false;
+}
+
+bool retro_is_emu_thread_initialized()
+{
+ return emu_thread_initialized;
+}
+
+void retro_join_emu_thread()
+{
+ static bool is_joined = false;
+ if (is_joined)
+ return;
+
+ pthread_join(emu_thread, NULL);
+ is_joined = true;
+}
+
+void retro_cancel_emu_thread()
+{
+ if (emu_thread_canceled)
+ return;
+
+ pthread_cancel(emu_thread);
+ emu_thread_canceled = true;
+}
+
+bool retro_emu_thread_exited()
+{
+ return emu_has_exited;
+}
+
+/*
+
+Copyright (C) 2020 Nikos Chantziaras <realnc@gmail.com>
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
diff --git a/retro_emu_thread.h b/retro_emu_thread.h
new file mode 100644
index 0000000..472ae8b
--- /dev/null
+++ b/retro_emu_thread.h
@@ -0,0 +1,51 @@
+#ifndef EMU_THREAD_H
+#define EMU_THREAD_H
+
+#include "common.h"
+
+/* gpSP doesn't have a top-level main loop that we can use, so instead we run it in its own thread
+ * and switch between it and the main thread. Calling this function will block the current thread
+ * and unblock the other.
+ *
+ * This function can be called from either the main or the emulation thread.
+ */
+void retro_switch_thread(void);
+
+/* Initialize the emulation thread and any related resources.
+ *
+ * Only call this function from the main thread.
+ */
+bool retro_init_emu_thread(bool dynarec, u32 cycles);
+
+/* Destroy the emulation thread and any related resources. Only call this after the emulation thread
+ * has finished (or canceled) and joined.
+ *
+ * Only call this function from the main thread.
+ */
+void retro_deinit_emu_thread(void);
+
+/* Returns true if the emulation thread was initialized successfully.
+ *
+ * This function can be called from either the main or the emulation thread.
+ */
+bool retro_is_emu_thread_initialized(void);
+
+/* Join the emulation thread. The thread must have exited naturally or been canceled.
+ *
+ * Only call this function from the main thread.
+ */
+void retro_join_emu_thread(void);
+
+/* Cancel the emulation thread.
+ *
+ * Only call this function from the main thread.
+ */
+void retro_cancel_emu_thread(void);
+
+/* Returns true if the emulation thread has exited naturally.
+ *
+ * This function can be called from either the main or the emulation thread.
+ */
+bool retro_emu_thread_exited(void);
+
+#endif