/* ** core.cpp ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** mkxp 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. ** ** mkxp 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 mkxp. If not, see . */ #include #include #include #include #include #include #include "mkxp-polyfill.h" // std::mutex #include "git-hash.h" #include "core.h" #include "binding-sandbox.h" #include "al-util.h" #include "audio.h" #include "eventthread.h" #include "filesystem.h" #include "gl-fun.h" #include "glstate.h" #include "graphics.h" #include "sharedmidistate.h" #include "sharedstate.h" #define THREADED_AUDIO_SAMPLES (((size_t)SYNTH_SAMPLERATE * (size_t)AUDIO_SLEEP) / (size_t)1000) using namespace mkxp_retro; using namespace mkxp_sandbox; struct lock_guard { std::mutex &mutex; lock_guard(std::mutex &mutex) : mutex(mutex) { mutex.lock(); } lock_guard(const struct lock_guard &guard) = delete; lock_guard(struct lock_guard &&guard) noexcept = delete; struct lock_guard &operator=(const struct lock_guard &guard) = delete; struct lock_guard &operator=(struct lock_guard &&guard) noexcept = delete; ~lock_guard() { mutex.unlock(); } }; template struct atomic { #ifdef MKXPZ_HAVE_THREADED_AUDIO std::atomic atom; #else T atom; #endif // MKXPZ_HAVE_THREADED_AUDIO atomic() {} atomic(T value) : atom(value) {} atomic(const struct atomic &guard) = delete; atomic(struct atomic &&guard) noexcept = delete; struct atomic &operator=(const struct atomic &guard) = delete; struct atomic &operator=(struct atomic &&guard) noexcept = delete; T load_relaxed() const noexcept { #ifdef MKXPZ_HAVE_THREADED_AUDIO return atom.load(std::memory_order_relaxed); #else return atom; #endif // MKXPZ_HAVE_THREADED_AUDIO } operator T() const noexcept { #ifdef MKXPZ_HAVE_THREADED_AUDIO return atom.load(std::memory_order_seq_cst); #else return atom; #endif // MKXPZ_HAVE_THREADED_AUDIO } void operator=(T value) noexcept { #ifdef MKXPZ_HAVE_THREADED_AUDIO atom.store(value, std::memory_order_seq_cst); #else atom = value; #endif // MKXPZ_HAVE_THREADED_AUDIO } void operator+=(T value) noexcept { #ifdef MKXPZ_HAVE_THREADED_AUDIO atom.fetch_add(value, std::memory_order_seq_cst); #else atom += value; #endif // MKXPZ_HAVE_THREADED_AUDIO } }; static uint64_t frame_count; static struct atomic frame_time; static uint64_t frame_time_remainder; static uint64_t retro_run_count; extern const uint8_t mkxp_retro_dist_zip[]; extern const size_t mkxp_retro_dist_zip_len; static bool initialized = false; static ALCdevice *al_device = NULL; static ALCcontext *al_context = NULL; static LPALCRENDERSAMPLESSOFT alcRenderSamplesSOFT = NULL; static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT = NULL; static int16_t *sound_buf = NULL; static bool retro_framebuffer_supported; static bool dupe_supported; static PHYSFS_File *rgssad = NULL; static retro_system_av_info av_info; static struct retro_audio_callback audio_callback; static struct retro_frame_time_callback frame_time_callback = { .callback = [](retro_usec_t delta) { frame_time += delta; frame_time_remainder += delta; }, }; static std::mutex threaded_audio_mutex; static bool threaded_audio_enabled = false; static bool frame_time_callback_enabled = false; static struct atomic shared_state_initialized(false); namespace mkxp_retro { retro_log_printf_t log_printf; retro_video_refresh_t video_refresh; retro_audio_sample_batch_t audio_sample_batch; retro_environment_t environment; retro_input_poll_t input_poll; retro_input_state_t input_state; struct retro_perf_callback perf; retro_hw_render_callback hw_render; bool keyboard_state[RETROK_LAST]; bool input_polled; uint64_t get_ticks_ms() noexcept { return frame_time / 1000; } uint64_t get_ticks_us() noexcept { return frame_time; } double get_refresh_rate() noexcept { return av_info.timing.fps; } bool using_threaded_audio() noexcept { return threaded_audio_enabled; } } static void fallback_log(enum retro_log_level level, const char *fmt, ...) { std::va_list va; va_start(va, fmt); std::vfprintf(stderr, fmt, va); va_end(va); } static void fluid_log(int level, const char *message, void *data) { switch (level) { case FLUID_PANIC: log_printf(RETRO_LOG_ERROR, "fluidsynth: panic: %s\n", message); break; case FLUID_ERR: log_printf(RETRO_LOG_ERROR, "fluidsynth: error: %s\n", message); break; case FLUID_WARN: log_printf(RETRO_LOG_WARN, "fluidsynth: warning: %s\n", message); break; case FLUID_INFO: log_printf(RETRO_LOG_INFO, "fluidsynth: %s\n", message); break; case FLUID_DBG: log_printf(RETRO_LOG_DEBUG, "fluidsynth: debug: %s\n", message); break; } } static uint32_t *frame_buf; boost::optional mkxp_retro::sandbox; boost::optional