mirror of https://github.com/sm64pc/sm64pc.git
fixed race condition (i hope). also created thread.h and .c for global access
This commit is contained in:
parent
89fa332a1f
commit
edf5b4ffc1
|
@ -13,6 +13,7 @@
|
||||||
#include "seq_ids.h"
|
#include "seq_ids.h"
|
||||||
#include "dialog_ids.h"
|
#include "dialog_ids.h"
|
||||||
#include "level_table.h"
|
#include "level_table.h"
|
||||||
|
#include "pc/thread.h"
|
||||||
|
|
||||||
#ifdef VERSION_EU
|
#ifdef VERSION_EU
|
||||||
#define EU_FLOAT(x) x ## f
|
#define EU_FLOAT(x) x ## f
|
||||||
|
@ -770,16 +771,18 @@ struct SPTask *create_next_audio_frame_task(void) {
|
||||||
|
|
||||||
void create_next_audio_buffer(s16 *samples, u32 num_samples) {
|
void create_next_audio_buffer(s16 *samples, u32 num_samples) {
|
||||||
gAudioFrameCount++;
|
gAudioFrameCount++;
|
||||||
// From sm64 decomp since that also uses a separate thread for audio
|
|
||||||
if (gAudioLoadLock != AUDIO_LOCK_NOT_LOADING) {
|
|
||||||
printf("Audio Thread: Lost 1 Frame.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sGameLoopTicked != 0) {
|
if (sGameLoopTicked != 0) {
|
||||||
update_game_sound();
|
update_game_sound();
|
||||||
sGameLoopTicked = 0;
|
sGameLoopTicked = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the game thread is resetting the sound, don't process any audio commands
|
||||||
|
pcthread_mutex_lock(&pcthread_game_mutex); bool reseting_sound = pcthread_game_reset_sound; pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
|
if (reseting_sound) {
|
||||||
|
printf("Audio thread: Dropped 1 frame\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
s32 writtenCmds;
|
s32 writtenCmds;
|
||||||
synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples);
|
synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples);
|
||||||
gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount);
|
gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount);
|
||||||
|
@ -2354,6 +2357,11 @@ void sound_reset(u8 presetId) {
|
||||||
sUnused8033323C = 0;
|
sUnused8033323C = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
// Wait for audio thread to finish rendering
|
||||||
|
pcthread_mutex_lock(&pcthread_game_mutex); pcthread_game_reset_sound = true; pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
|
pcthread_mutex_lock(&pcthread_audio_mutex); bool rendering = pcthread_audio_rendering; pcthread_mutex_unlock(&pcthread_audio_mutex);
|
||||||
|
if (rendering) pcthread_semaphore_wait(&pcthread_audio_sema);
|
||||||
|
|
||||||
sGameLoopTicked = 0;
|
sGameLoopTicked = 0;
|
||||||
disable_all_sequence_players();
|
disable_all_sequence_players();
|
||||||
sound_init();
|
sound_init();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "pc/platform.h"
|
#include "pc/platform.h"
|
||||||
#include "pc/fs/fs.h"
|
#include "pc/fs/fs.h"
|
||||||
|
#include "pc/thread.h"
|
||||||
|
|
||||||
#define ALIGN16(val) (((val) + 0xF) & ~0xF)
|
#define ALIGN16(val) (((val) + 0xF) & ~0xF)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "seqplayer.h"
|
#include "seqplayer.h"
|
||||||
#include "synthesis.h"
|
#include "synthesis.h"
|
||||||
|
#include "pc/thread.h"
|
||||||
|
|
||||||
#ifdef VERSION_EU
|
#ifdef VERSION_EU
|
||||||
|
|
||||||
|
@ -54,6 +55,13 @@ void create_next_audio_buffer(s16 *samples, u32 num_samples) {
|
||||||
if (osRecvMesg(OSMesgQueues[1], &msg, OS_MESG_NOBLOCK) != -1) {
|
if (osRecvMesg(OSMesgQueues[1], &msg, OS_MESG_NOBLOCK) != -1) {
|
||||||
func_802ad7ec((u32) msg);
|
func_802ad7ec((u32) msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the game thread is resetting the sound, don't process any audio commands
|
||||||
|
pcthread_mutex_lock(&pcthread_game_mutex); bool reseting_sound = pcthread_game_reset_sound; pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
|
if (reseting_sound) {
|
||||||
|
printf("Audio thread: Dropped 1 frame\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples);
|
synthesis_execute(gAudioCmdBuffers[0], &writtenCmds, samples, num_samples);
|
||||||
gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount);
|
gAudioRandom = ((gAudioRandom + gAudioFrameCount) * gAudioFrameCount);
|
||||||
gAudioRandom = gAudioRandom + writtenCmds / 8;
|
gAudioRandom = gAudioRandom + writtenCmds / 8;
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h> //Header file for sleep(). man 3 sleep for details.
|
#include <unistd.h> //Header file for sleep(). man 3 sleep for details.
|
||||||
#include <pthread.h>
|
|
||||||
#include <semaphore.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
@ -30,6 +28,7 @@
|
||||||
#include "audio/audio_null.h"
|
#include "audio/audio_null.h"
|
||||||
|
|
||||||
#include "pc_main.h"
|
#include "pc_main.h"
|
||||||
|
#include "thread.h"
|
||||||
#include "cliopts.h"
|
#include "cliopts.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "controller/controller_api.h"
|
#include "controller/controller_api.h"
|
||||||
|
@ -61,14 +60,16 @@ static struct AudioAPI *audio_api;
|
||||||
static struct GfxWindowManagerAPI *wm_api;
|
static struct GfxWindowManagerAPI *wm_api;
|
||||||
static struct GfxRenderingAPI *rendering_api;
|
static struct GfxRenderingAPI *rendering_api;
|
||||||
|
|
||||||
pthread_t audio_thread_id;
|
pthread_t pcthread_audio_id;
|
||||||
pthread_mutex_t audio_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t pcthread_audio_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
bool audio_thread_running = false;
|
bool pcthread_audio_init = false;
|
||||||
sem_t audio_sem;
|
bool pcthread_audio_rendering = false;
|
||||||
|
sem_t pcthread_audio_sema;
|
||||||
|
|
||||||
pthread_mutex_t game_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t pcthread_game_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
bool game_loop_iterating = false;
|
bool pcthread_game_loop_iterating = false;
|
||||||
sem_t game_sem;
|
bool pcthread_game_reset_sound = false;
|
||||||
|
sem_t pcthread_game_sema;
|
||||||
|
|
||||||
extern void gfx_run(Gfx *commands);
|
extern void gfx_run(Gfx *commands);
|
||||||
extern void thread5_game_loop(void *arg);
|
extern void thread5_game_loop(void *arg);
|
||||||
|
@ -100,49 +101,46 @@ void send_display_list(struct SPTask *spTask) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void lock_game_loop(bool unlock) {
|
void lock_game_loop(bool unlock) {
|
||||||
sys_mutex_lock(&game_mutex);
|
pcthread_mutex_lock(&pcthread_game_mutex);
|
||||||
game_loop_iterating = unlock;
|
pcthread_game_loop_iterating = unlock;
|
||||||
sys_mutex_unlock(&game_mutex);
|
pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool game_loop_locked() {
|
bool game_loop_locked() {
|
||||||
sys_mutex_lock(&game_mutex);
|
pcthread_mutex_lock(&pcthread_game_mutex);
|
||||||
bool locked = game_loop_iterating;
|
bool locked = pcthread_game_loop_iterating;
|
||||||
sys_mutex_unlock(&game_mutex);
|
pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
return locked;
|
return locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
void produce_one_frame(void) {
|
void produce_one_frame(void) {
|
||||||
gfx_start_frame();
|
gfx_start_frame();
|
||||||
|
|
||||||
sys_semaphore_wait(&audio_sem);
|
|
||||||
game_loop_one_iteration();
|
game_loop_one_iteration();
|
||||||
sys_semaphore_post(&game_sem);
|
|
||||||
|
// Post the game thread semaphore if the game requested a sound reset
|
||||||
|
pcthread_mutex_lock(&pcthread_game_mutex);
|
||||||
|
if (pcthread_game_reset_sound) {
|
||||||
|
pcthread_semaphore_post(&pcthread_game_sema);
|
||||||
|
pcthread_game_reset_sound = false;
|
||||||
|
}
|
||||||
|
pcthread_mutex_unlock(&pcthread_game_mutex);
|
||||||
|
|
||||||
thread6_rumble_loop(NULL);
|
thread6_rumble_loop(NULL);
|
||||||
|
|
||||||
gfx_end_frame();
|
gfx_end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seperate the audio thread from the main thread so that your ears won't bleed at a low framerate
|
// Seperate the audio thread from the main thread so that your ears won't bleed at a low framerate
|
||||||
// BUG: Race condition between loading zones when the game logic runs faster than its intended speed.
|
// RACE CONDITION:
|
||||||
// What I think happens is that the game thread finishes loading new music before the audio thread finishes rendering the audio.
|
|
||||||
// This won't happen in normal gameplay at all unless you were to uncap the framerate, which you can only do by modifying the source code.
|
|
||||||
void* audio_thread() {
|
void* audio_thread() {
|
||||||
// Keep track of the time in microseconds
|
// Keep track of the time in microseconds
|
||||||
const f64 frametime_micro = 16666.666; // 16.666666 ms = 60Hz; run this thread 60 times a second like the original game
|
const f64 frametime_micro = 16666.666; // 16.666666 ms = 60Hz; run this thread 60 times a second like the original game
|
||||||
f64 start_time;
|
f64 start_time;
|
||||||
f64 end_time;
|
f64 end_time;
|
||||||
sys_semaphore_wait(&game_sem);
|
bool doloop = true;
|
||||||
while(1) {
|
pcthread_semaphore_wait(&pcthread_game_sema);
|
||||||
// Check if the audio thread should be stopped
|
while(doloop) {
|
||||||
sys_mutex_lock(&audio_mutex);
|
|
||||||
if (!audio_thread_running) {
|
|
||||||
sys_mutex_unlock(&audio_mutex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sys_mutex_unlock(&audio_mutex);
|
|
||||||
|
|
||||||
start_time = sys_profile_time();
|
start_time = sys_profile_time();
|
||||||
const f32 master_mod = (f32)configMasterVolume / 127.0f;
|
const f32 master_mod = (f32)configMasterVolume / 127.0f;
|
||||||
set_sequence_player_volume(SEQ_PLAYER_LEVEL, (f32)configMusicVolume / 127.0f * master_mod);
|
set_sequence_player_volume(SEQ_PLAYER_LEVEL, (f32)configMusicVolume / 127.0f * master_mod);
|
||||||
|
@ -157,7 +155,11 @@ void* audio_thread() {
|
||||||
audio_cnt = 2;
|
audio_cnt = 2;
|
||||||
}
|
}
|
||||||
u32 num_audio_samples = audio_cnt < 2 ? 528 : 544;*/
|
u32 num_audio_samples = audio_cnt < 2 ? 528 : 544;*/
|
||||||
create_next_audio_buffer(audio_buffer, num_audio_samples);
|
|
||||||
|
pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_rendering = true; pcthread_mutex_unlock(&pcthread_audio_mutex);
|
||||||
|
create_next_audio_buffer(audio_buffer, num_audio_samples);
|
||||||
|
pcthread_semaphore_post(&pcthread_audio_sema);
|
||||||
|
pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_rendering = false; pcthread_mutex_unlock(&pcthread_audio_mutex);
|
||||||
|
|
||||||
// printf("Audio samples before submitting: %d\n", audio_api->buffered());
|
// printf("Audio samples before submitting: %d\n", audio_api->buffered());
|
||||||
audio_api->play((u8 *)audio_buffer, num_audio_samples * 4);
|
audio_api->play((u8 *)audio_buffer, num_audio_samples * 4);
|
||||||
|
@ -168,25 +170,25 @@ void* audio_thread() {
|
||||||
f64 nap_time = frametime_micro - (end_time - start_time);
|
f64 nap_time = frametime_micro - (end_time - start_time);
|
||||||
// printf("Audio thread nap time: %f\n", nap_time);
|
// printf("Audio thread nap time: %f\n", nap_time);
|
||||||
if (nap_time > 0.0) sys_sleep(nap_time);
|
if (nap_time > 0.0) sys_sleep(nap_time);
|
||||||
sys_semaphore_post(&audio_sem);
|
|
||||||
|
// Check if the game thread is still running
|
||||||
|
pcthread_mutex_lock(&pcthread_audio_mutex); doloop = pcthread_audio_init; pcthread_mutex_unlock(&pcthread_audio_mutex);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_thread_init() {
|
void audio_thread_init() {
|
||||||
audio_thread_running = true;
|
pcthread_audio_init = true;
|
||||||
sys_semaphore_init(&audio_sem, 0, 1);
|
pcthread_semaphore_init(&pcthread_audio_sema, 0, 1);
|
||||||
pthread_create(&audio_thread_id, NULL, audio_thread, NULL);
|
pthread_create(&pcthread_audio_id, NULL, audio_thread, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_shutdown(void) {
|
void audio_shutdown(void) {
|
||||||
// Tell the audio thread to stop
|
// Tell the audio thread to stop
|
||||||
sys_mutex_lock(&audio_mutex);
|
pcthread_mutex_lock(&pcthread_audio_mutex); pcthread_audio_init = false; pcthread_mutex_unlock(&pcthread_audio_mutex);
|
||||||
audio_thread_running = false;
|
pcthread_semaphore_wait(&pcthread_audio_sema); // Wait for the audio thread to finish rendering audio, then destroy it all
|
||||||
sys_mutex_unlock(&audio_mutex);
|
pthread_join(pcthread_audio_id, NULL);
|
||||||
sys_semaphore_wait(&audio_sem); // Wait for the audio thread to finish rendering audio, then destroy it all
|
pcthread_semaphore_destroy(&pcthread_audio_sema);
|
||||||
pthread_join(audio_thread_id, NULL);
|
|
||||||
sys_semaphore_destroy(&audio_sem);
|
|
||||||
|
|
||||||
if (audio_api) {
|
if (audio_api) {
|
||||||
if (audio_api->shutdown) audio_api->shutdown();
|
if (audio_api->shutdown) audio_api->shutdown();
|
||||||
|
@ -202,7 +204,7 @@ void game_deinit(void) {
|
||||||
controller_shutdown();
|
controller_shutdown();
|
||||||
audio_shutdown();
|
audio_shutdown();
|
||||||
gfx_shutdown();
|
gfx_shutdown();
|
||||||
sys_semaphore_destroy(&game_sem);
|
pcthread_semaphore_destroy(&pcthread_game_sema);
|
||||||
inited = false;
|
inited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +336,7 @@ void main_func(void) {
|
||||||
request_anim_frame(on_anim_frame);
|
request_anim_frame(on_anim_frame);
|
||||||
#else
|
#else
|
||||||
// initialize multithreading
|
// initialize multithreading
|
||||||
sys_semaphore_init(&game_sem, 0, 0);
|
pcthread_semaphore_init(&pcthread_game_sema, 0, 0);
|
||||||
audio_thread_init();
|
audio_thread_init();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -81,29 +81,6 @@ double sys_profile_time(void) {
|
||||||
return (double)(tv.tv_nsec) / 1000.0 + (double)(tv.tv_sec) * 1000000.0;
|
return (double)(tv.tv_nsec) / 1000.0 + (double)(tv.tv_sec) * 1000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_mutex_lock(pthread_mutex_t *mutex) {
|
|
||||||
pthread_mutex_lock(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sys_mutex_unlock(pthread_mutex_t *mutex) {
|
|
||||||
pthread_mutex_unlock(mutex);
|
|
||||||
}
|
|
||||||
void sys_semaphore_init(sem_t *sem, int pshared, unsigned int value) {
|
|
||||||
sem_init(sem, pshared, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sys_semaphore_wait(sem_t *sem) {
|
|
||||||
sem_wait(sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sys_semaphore_post(sem_t *sem) {
|
|
||||||
sem_post(sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sys_semaphore_destroy(sem_t *sem) {
|
|
||||||
sem_destroy(sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this calls a platform-specific impl function after forming the error message */
|
/* this calls a platform-specific impl function after forming the error message */
|
||||||
|
|
||||||
static void sys_fatal_impl(const char *msg) __attribute__ ((noreturn));
|
static void sys_fatal_impl(const char *msg) __attribute__ ((noreturn));
|
||||||
|
|
|
@ -22,12 +22,6 @@ char *sys_strlwr(char *src);
|
||||||
int sys_strcasecmp(const char *s1, const char *s2);
|
int sys_strcasecmp(const char *s1, const char *s2);
|
||||||
void sys_sleep(const uint64_t us);
|
void sys_sleep(const uint64_t us);
|
||||||
double sys_profile_time(void);
|
double sys_profile_time(void);
|
||||||
void sys_mutex_lock(pthread_mutex_t *mutex);
|
|
||||||
void sys_mutex_unlock(pthread_mutex_t *mutex);
|
|
||||||
void sys_semaphore_init(sem_t *sem, int pshared, unsigned int value);
|
|
||||||
void sys_semaphore_wait(sem_t *sem);
|
|
||||||
void sys_semaphore_post(sem_t *sem);
|
|
||||||
void sys_semaphore_destroy(sem_t *sem);
|
|
||||||
|
|
||||||
// path stuff
|
// path stuff
|
||||||
const char *sys_user_path(void);
|
const char *sys_user_path(void);
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
|
void pcthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||||
|
pthread_mutex_lock(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||||
|
pthread_mutex_unlock(mutex);
|
||||||
|
}
|
||||||
|
void pcthread_semaphore_init(sem_t *sem, int pshared, unsigned int value) {
|
||||||
|
sem_init(sem, pshared, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcthread_semaphore_wait(sem_t *sem) {
|
||||||
|
sem_wait(sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcthread_semaphore_post(sem_t *sem) {
|
||||||
|
sem_post(sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcthread_semaphore_destroy(sem_t *sem) {
|
||||||
|
sem_destroy(sem);
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#ifndef _PC_THREAD_H
|
||||||
|
#define _PC_THREAD_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern pthread_t pcthread_audio_id;
|
||||||
|
extern pthread_mutex_t pcthread_audio_mutex;
|
||||||
|
extern sem_t pcthread_audio_sema;
|
||||||
|
extern bool pcthread_audio_init;
|
||||||
|
extern bool pcthread_audio_rendering;
|
||||||
|
|
||||||
|
extern pthread_mutex_t pcthread_game_mutex;
|
||||||
|
extern sem_t pcthread_game_sema;
|
||||||
|
extern bool pcthread_game_loop_iterating;
|
||||||
|
extern bool pcthread_game_reset_sound;
|
||||||
|
|
||||||
|
void pcthread_mutex_lock(pthread_mutex_t *mutex);
|
||||||
|
void pcthread_mutex_unlock(pthread_mutex_t *mutex);
|
||||||
|
void pcthread_semaphore_init(sem_t *sem, int pshared, unsigned int value);
|
||||||
|
void pcthread_semaphore_wait(sem_t *sem);
|
||||||
|
void pcthread_semaphore_post(sem_t *sem);
|
||||||
|
void pcthread_semaphore_destroy(sem_t *sem);
|
||||||
|
|
||||||
|
#endif // _PC_THREAD_H
|
Loading…
Reference in New Issue