Merge pull request #81 from jdgleaver/libco-alt
Add build-time option to run the emulator in a thread instead of libco (fixes OpenDingux target)
This commit is contained in:
commit
f5eae17f5a
17
Makefile
17
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)
|
||||
|
||||
|
@ -400,13 +401,13 @@ else ifeq ($(platform), emscripten)
|
|||
# GCW0
|
||||
else ifeq ($(platform), gcw0)
|
||||
TARGET := $(TARGET_NAME)_libretro.so
|
||||
CC ?= /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
CXX ?= /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
|
||||
AR ?= /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar
|
||||
CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
CXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
39
libretro.c
39
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();
|
||||
|
|
|
@ -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/>.
|
||||
|
||||
*/
|
|
@ -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
|
Loading…
Reference in New Issue