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:
Autechre 2020-10-23 17:34:42 +02:00 committed by GitHub
commit f5eae17f5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 282 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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();

175
retro_emu_thread.c Normal file
View File

@ -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/>.
*/

51
retro_emu_thread.h Normal file
View File

@ -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