aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Weiss2020-02-21 21:17:31 -0800
committerJustin Weiss2020-02-25 22:58:22 -0800
commit679b71e250ce6557f05b57882747029ab6af7edc (patch)
tree4a7d690b6c3b3cf0d2e7ae8eb0be1dbd2a992990
parent58e485d6efcecfe0fb7d072d705cf658e6162b4e (diff)
downloadpcsx_rearmed-679b71e250ce6557f05b57882747029ab6af7edc.tar.gz
pcsx_rearmed-679b71e250ce6557f05b57882747029ab6af7edc.tar.bz2
pcsx_rearmed-679b71e250ce6557f05b57882747029ab6af7edc.zip
Add async CD access
-rw-r--r--Makefile2
-rw-r--r--frontend/3ds/3ds_utils.h14
-rw-r--r--frontend/3ds/pthread.h81
-rw-r--r--frontend/libretro.c12
-rw-r--r--frontend/libretro_core_options.h14
-rw-r--r--libpcsxcore/cdriso.c251
-rw-r--r--libpcsxcore/psxcommon.h1
7 files changed, 371 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index f633665..9866050 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ CFLAGS += -Wall -Iinclude -ffast-math
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -ggdb
else
-ifeq ($(platform), vita)
+ifeq ($(platform), $(filter $(platform), vita ctr))
CFLAGS += -O3 -DNDEBUG
else
CFLAGS += -O2 -DNDEBUG
diff --git a/frontend/3ds/3ds_utils.h b/frontend/3ds/3ds_utils.h
index cad4cbe..c9b9d7f 100644
--- a/frontend/3ds/3ds_utils.h
+++ b/frontend/3ds/3ds_utils.h
@@ -24,6 +24,20 @@ void threadFree(int32_t thread);
void threadExit(int32_t rc) __attribute__((noreturn));
int32_t svcGetSystemInfo(int64_t* out, uint32_t type, int32_t param);
+
+int32_t svcCreateSemaphore(uint32_t *sem, int32_t initial_count, uint32_t max_count);
+int32_t svcReleaseSemaphore(int32_t *count, uint32_t sem, int32_t release_count);
+int32_t svcWaitSynchronization(uint32_t handle, int64_t nanoseconds);
+
+typedef int32_t LightLock;
+
+void LightLock_Init(LightLock* lock);
+void LightLock_Lock(LightLock* lock);
+int LightLock_TryLock(LightLock* lock);
+void LightLock_Unlock(LightLock* lock);
+
+int32_t APT_CheckNew3DS(bool *out);
+
int32_t svcBackdoor(int32_t (*callback)(void));
#define DEBUG_HOLD() do{printf("%s@%s:%d.\n",__FUNCTION__, __FILE__, __LINE__);fflush(stdout);wait_for_input();}while(0)
diff --git a/frontend/3ds/pthread.h b/frontend/3ds/pthread.h
index 9f43707..cc3c965 100644
--- a/frontend/3ds/pthread.h
+++ b/frontend/3ds/pthread.h
@@ -9,15 +9,34 @@
#include "3ds_utils.h"
#define CTR_PTHREAD_STACK_SIZE 0x10000
+#define FALSE 0
typedef int32_t pthread_t;
typedef int pthread_attr_t;
+typedef LightLock pthread_mutex_t;
+typedef int pthread_mutexattr_t;
+
+typedef struct {
+ uint32_t semaphore;
+ LightLock lock;
+ uint32_t waiting;
+} pthread_cond_t;
+
+typedef int pthread_condattr_t;
+
static inline int pthread_create(pthread_t *thread,
const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg)
{
- thread = threadCreate(start_routine, arg, CTR_PTHREAD_STACK_SIZE, 0x25, -2, FALSE);
- return 1;
+ int procnum = -2; // use default cpu
+ bool isNew3DS;
+ APT_CheckNew3DS(&isNew3DS);
+
+ if (isNew3DS)
+ procnum = 2;
+
+ *thread = threadCreate(start_routine, arg, CTR_PTHREAD_STACK_SIZE, 0x25, procnum, FALSE);
+ return 0;
}
@@ -41,6 +60,64 @@ static inline void pthread_exit(void *retval)
threadExit(0);
}
+static inline int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) {
+ LightLock_Init(mutex);
+ return 0;
+}
+
+static inline int pthread_mutex_lock(pthread_mutex_t *mutex) {
+ LightLock_Lock(mutex);
+ return 0;
+}
+
+static inline int pthread_mutex_unlock(pthread_mutex_t *mutex) {
+ LightLock_Unlock(mutex);
+ return 0;
+}
+
+static inline int pthread_mutex_destroy(pthread_mutex_t *mutex) {
+ return 0;
+}
+
+static inline int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
+ if (svcCreateSemaphore(&cond->semaphore, 0, 1))
+ goto error;
+
+ LightLock_Init(&cond->lock);
+ cond->waiting = 0;
+ return 0;
+
+ error:
+ svcCloseHandle(cond->semaphore);
+ return -1;
+}
+
+static inline int pthread_cond_signal(pthread_cond_t *cond) {
+ int32_t count;
+ LightLock_Lock(&cond->lock);
+ if (cond->waiting) {
+ cond->waiting--;
+ svcReleaseSemaphore(&count, cond->semaphore, 1);
+ }
+ LightLock_Unlock(&cond->lock);
+ return 0;
+}
+
+static inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *lock) {
+ LightLock_Lock(&cond->lock);
+ cond->waiting++;
+ LightLock_Unlock(lock);
+ LightLock_Unlock(&cond->lock);
+ svcWaitSynchronization(cond->semaphore, INT64_MAX);
+ LightLock_Lock(lock);
+ return 0;
+}
+
+static inline int pthread_cond_destroy(pthread_cond_t *cond) {
+ svcCloseHandle(cond->semaphore);
+ return 0;
+}
+
#endif //_3DS_PTHREAD_WRAP__
diff --git a/frontend/libretro.c b/frontend/libretro.c
index cc443fe..bd7dc43 100644
--- a/frontend/libretro.c
+++ b/frontend/libretro.c
@@ -1852,6 +1852,18 @@ static void update_variables(bool in_flight)
Config.VSyncWA = 1;
}
+#ifndef _WIN32
+ var.value = NULL;
+ var.key = "pcsx_rearmed_async_cd";
+ if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+ {
+ if (strcmp(var.value, "async") == 0)
+ Config.AsyncCD = 1;
+ else
+ Config.AsyncCD = 0;
+ }
+#endif
+
var.value = NULL;
var.key = "pcsx_rearmed_noxadecoding";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
diff --git a/frontend/libretro_core_options.h b/frontend/libretro_core_options.h
index badd856..6f26c71 100644
--- a/frontend/libretro_core_options.h
+++ b/frontend/libretro_core_options.h
@@ -961,7 +961,19 @@ struct retro_core_option_definition option_defs_us[] = {
},
"disabled",
},
-
+#ifndef _WIN32
+ {
+ "pcsx_rearmed_async_cd",
+ "CD Access Method (Restart)",
+ "Select method used to read data from content disk images. 'Synchronous' mimics original hardware. 'Asynchronous' can reduce stuttering on devices with slow storage.",
+ {
+ { "sync", "Synchronous" },
+ { "async", "Asynchronous" },
+ { NULL, NULL},
+ },
+ "sync",
+ },
+#endif
/* ADVANCED OPTIONS */
{
"pcsx_rearmed_noxadecoding",
diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c
index 8171674..07adbe6 100644
--- a/libpcsxcore/cdriso.c
+++ b/libpcsxcore/cdriso.c
@@ -1163,6 +1163,194 @@ static int opensbifile(const char *isoname) {
return LoadSBI(sbiname, s);
}
+#ifdef _WIN32
+static void readThreadStop() {}
+static void readThreadStart() {}
+#else
+static pthread_t read_thread_id;
+
+static pthread_cond_t read_thread_msg_avail;
+static pthread_cond_t read_thread_msg_done;
+static pthread_mutex_t read_thread_msg_lock;
+
+static pthread_cond_t sectorbuffer_cond;
+static pthread_mutex_t sectorbuffer_lock;
+
+static boolean read_thread_running = FALSE;
+static int read_thread_sector_start = -1;
+static int read_thread_sector_end = -1;
+
+typedef struct {
+ int sector;
+ long ret;
+ unsigned char data[CD_FRAMESIZE_RAW];
+} SectorBufferEntry;
+
+#define SECTOR_BUFFER_SIZE 4096
+
+static SectorBufferEntry *sectorbuffer;
+static size_t sectorbuffer_index;
+
+int (*sync_cdimg_read_func)(FILE *f, unsigned int base, void *dest, int sector);
+unsigned char *(*sync_CDR_getBuffer)(void);
+
+static unsigned char * CALLBACK ISOgetBuffer_async(void);
+static int cdread_async(FILE *f, unsigned int base, void *dest, int sector);
+
+static void *readThreadMain(void *param) {
+ int max_sector = -1;
+ int requested_sector_start = -1;
+ int requested_sector_end = -1;
+ int last_read_sector = -1;
+ int index = 0;
+
+ int ra_sector = -1;
+ int max_ra = 128;
+ int initial_ra = 1;
+ int speedmult_ra = 4;
+
+ int ra_count = 0;
+ int how_far_ahead = 0;
+
+ unsigned char tmpdata[CD_FRAMESIZE_RAW];
+ long ret;
+
+ max_sector = msf2sec(ti[numtracks].start) + msf2sec(ti[numtracks].length);
+
+ while(1) {
+ pthread_mutex_lock(&read_thread_msg_lock);
+
+ // If we don't have readahead and we don't have a sector request, wait for one.
+ // If we still have readahead to go, don't block, just keep going.
+ // And if we ever have a sector request pending, acknowledge and reset it.
+
+ if (!ra_count) {
+ if (read_thread_sector_start == -1 && read_thread_running) {
+ pthread_cond_wait(&read_thread_msg_avail, &read_thread_msg_lock);
+ }
+ }
+
+ if (read_thread_sector_start != -1) {
+ requested_sector_start = read_thread_sector_start;
+ requested_sector_end = read_thread_sector_end;
+ read_thread_sector_start = -1;
+ read_thread_sector_end = -1;
+ pthread_cond_signal(&read_thread_msg_done);
+ }
+
+ pthread_mutex_unlock(&read_thread_msg_lock);
+
+ if (!read_thread_running)
+ break;
+
+ // Readahead code, based on the implementation in mednafen psx's cdromif.cpp
+ if (requested_sector_start != -1) {
+ if (last_read_sector != -1 && last_read_sector == (requested_sector_start - 1)) {
+ how_far_ahead = ra_sector - requested_sector_end;
+
+ if(how_far_ahead <= max_ra)
+ ra_count = (max_ra - how_far_ahead + 1 ? max_ra - how_far_ahead + 1 : speedmult_ra);
+ else
+ ra_count++;
+ } else if (requested_sector_end != last_read_sector) {
+ ra_sector = requested_sector_end;
+ ra_count = initial_ra;
+ }
+
+ last_read_sector = requested_sector_end;
+ }
+
+ // check for end of CD
+ if (ra_count && ra_sector >= max_sector) {
+ ra_count = 0;
+ }
+
+ if (ra_count) {
+
+ index = ra_sector % SECTOR_BUFFER_SIZE;
+ pthread_mutex_lock(&sectorbuffer_lock);
+ if (sectorbuffer[index].sector != ra_sector) {
+ pthread_mutex_unlock(&sectorbuffer_lock);
+
+ ret = sync_cdimg_read_func(cdHandle, 0, tmpdata, ra_sector);
+
+ pthread_mutex_lock(&sectorbuffer_lock);
+ sectorbuffer[index].ret = ret;
+ sectorbuffer[index].sector = ra_sector;
+ memcpy(sectorbuffer[index].data, tmpdata, CD_FRAMESIZE_RAW);
+ }
+ pthread_cond_signal(&sectorbuffer_cond);
+ pthread_mutex_unlock(&sectorbuffer_lock);
+
+ ra_sector++;
+ ra_count--;
+ }
+ }
+
+ return NULL;
+}
+
+static void readThreadStop() {
+ if (read_thread_running == TRUE) {
+ read_thread_running = FALSE;
+ pthread_cond_signal(&read_thread_msg_avail);
+ pthread_join(read_thread_id, NULL);
+ }
+
+ pthread_cond_destroy(&read_thread_msg_done);
+ pthread_cond_destroy(&read_thread_msg_avail);
+ pthread_mutex_destroy(&read_thread_msg_lock);
+
+ pthread_cond_destroy(&sectorbuffer_cond);
+ pthread_mutex_destroy(&sectorbuffer_lock);
+
+ CDR_getBuffer = sync_CDR_getBuffer;
+ cdimg_read_func = sync_cdimg_read_func;
+
+ free(sectorbuffer);
+ sectorbuffer = NULL;
+}
+
+static void readThreadStart() {
+ SysPrintf("Starting async CD thread\n");
+
+ if (read_thread_running == TRUE)
+ return;
+
+ read_thread_running = TRUE;
+ read_thread_sector_start = -1;
+ read_thread_sector_end = -1;
+ sectorbuffer_index = 0;
+
+ sectorbuffer = calloc(SECTOR_BUFFER_SIZE, sizeof(SectorBufferEntry));
+ if(!sectorbuffer)
+ goto error;
+
+ sectorbuffer[0].sector = -1; // Otherwise we might think we've already fetched sector 0!
+
+ sync_CDR_getBuffer = CDR_getBuffer;
+ CDR_getBuffer = ISOgetBuffer_async;
+ sync_cdimg_read_func = cdimg_read_func;
+ cdimg_read_func = cdread_async;
+
+ if (pthread_cond_init(&read_thread_msg_avail, NULL) ||
+ pthread_cond_init(&read_thread_msg_done, NULL) ||
+ pthread_mutex_init(&read_thread_msg_lock, NULL) ||
+ pthread_cond_init(&sectorbuffer_cond, NULL) ||
+ pthread_mutex_init(&sectorbuffer_lock, NULL) ||
+ pthread_create(&read_thread_id, NULL, readThreadMain, NULL))
+ goto error;
+
+ return;
+
+ error:
+ SysPrintf("Error starting async CD thread\n");
+ SysPrintf("Falling back to sync\n");
+
+ readThreadStop();
+}
+#endif
+
static int cdread_normal(FILE *f, unsigned int base, void *dest, int sector)
{
fseek(f, base + sector * CD_FRAMESIZE_RAW, SEEK_SET);
@@ -1323,6 +1511,54 @@ static int cdread_2048(FILE *f, unsigned int base, void *dest, int sector)
return ret;
}
+#ifndef _WIN32
+
+static int cdread_async(FILE *f, unsigned int base, void *dest, int sector) {
+ boolean found = FALSE;
+ int i = sector % SECTOR_BUFFER_SIZE;
+ long ret;
+
+ if (f != cdHandle || base != 0 || dest != cdbuffer) {
+ // Async reads are only supported for cdbuffer, so call the sync
+ // function directly.
+ return sync_cdimg_read_func(f, base, dest, sector);
+ }
+
+ pthread_mutex_lock(&read_thread_msg_lock);
+
+ // Only wait if we're not trying to read the next sector and
+ // sector_start is set (meaning the last request hasn't been
+ // processed yet)
+ while(read_thread_sector_start != -1 && read_thread_sector_end + 1 != sector) {
+ pthread_cond_wait(&read_thread_msg_done, &read_thread_msg_lock);
+ }
+
+ if (read_thread_sector_start == -1)
+ read_thread_sector_start = sector;
+
+ read_thread_sector_end = sector;
+ pthread_cond_signal(&read_thread_msg_avail);
+ pthread_mutex_unlock(&read_thread_msg_lock);
+
+ do {
+ pthread_mutex_lock(&sectorbuffer_lock);
+ if (sectorbuffer[i].sector == sector) {
+ sectorbuffer_index = i;
+ ret = sectorbuffer[i].ret;
+ found = TRUE;
+ }
+
+ if (!found) {
+ pthread_cond_wait(&sectorbuffer_cond, &sectorbuffer_lock);
+ }
+ pthread_mutex_unlock(&sectorbuffer_lock);
+ } while (!found);
+
+ return ret;
+}
+
+#endif
+
static unsigned char * CALLBACK ISOgetBuffer_compr(void) {
return compr_img->buff_raw[compr_img->sector_in_blk] + 12;
}
@@ -1333,6 +1569,17 @@ static unsigned char * CALLBACK ISOgetBuffer_chd(void) {
}
#endif
+#ifndef _WIN32
+static unsigned char * CALLBACK ISOgetBuffer_async(void) {
+ unsigned char *buffer;
+ pthread_mutex_lock(&sectorbuffer_lock);
+ buffer = sectorbuffer[sectorbuffer_index].data;
+ pthread_mutex_unlock(&sectorbuffer_lock);
+ return buffer + 12;
+}
+
+#endif
+
static unsigned char * CALLBACK ISOgetBuffer(void) {
return cdbuffer + 12;
}
@@ -1473,6 +1720,9 @@ static long CALLBACK ISOopen(void) {
cdda_cur_sector = 0;
cdda_file_offset = 0;
+ if (Config.AsyncCD) {
+ readThreadStart();
+ }
return 0;
}
@@ -1518,6 +1768,7 @@ static long CALLBACK ISOclose(void) {
memset(cdbuffer, 0, sizeof(cdbuffer));
CDR_getBuffer = ISOgetBuffer;
+ readThreadStop();
return 0;
}
diff --git a/libpcsxcore/psxcommon.h b/libpcsxcore/psxcommon.h
index 19c8d77..cb417d4 100644
--- a/libpcsxcore/psxcommon.h
+++ b/libpcsxcore/psxcommon.h
@@ -120,6 +120,7 @@ typedef struct {
boolean Mdec;
boolean PsxAuto;
boolean Cdda;
+ boolean AsyncCD;
boolean HLE;
boolean SlowBoot;
boolean Debug;