Files
parallel-gx1/Source/Core/DolphinLibretro/Main.cpp
T
LibretroAdmin e4bac5f75c Shaders: ubershaders by default, seeded per game
Shader compilation defaults to Async (UberShaders): a draw whose own
pipeline is not built yet is drawn with an ubershader one, exact
rendering, while its own builds on the compiler threads, which now
number as many as the machine has to spare (cores - 3, 1 to 4) instead
of one.

What is left to stutter is an ubershader pipeline itself, built on the
spot the first time a draw needs one: one per vertex layout and fixed
function state. Every ubershader pipeline a draw uses is now recorded,
in <game id>.uberuidcache next to the specialized UID cache, and those
are built before the game starts the next time it boots; every other
ubershader pipeline there can be, and the specialized pipelines known,
build in the background while it runs instead of before it starts,
which took 21 s on lavapipe. The record is written when the cache
closes, into room reserved when it opens, so recording allocates
nothing in the middle of a game.

Without compiler threads (OpenGL here, with no shared context for
them), asking for a pipeline in the background builds it on the spot
and an ubershader one besides: the asynchronous modes draw as
Synchronous does then, and nothing extra is built before the game
starts.

Booting a FIFO log no longer hangs: the CPU thread clears the EFB, which
waits on the GPU, before it reaches its run loop, and the frontend's
thread waited for that run loop without running the GPU. It runs the
GPU until then.
2026-11-06 23:07:17 +01:00

866 lines
30 KiB
C++

#include <features/features_cpu.h>
#include <cmath>
#include <cstdint>
#include <libretro.h>
#include <string>
#include <fstream>
#include <vector>
#include "Common/ChunkFile.h"
#include "Common/Event.h"
#include "Common/FrameClock.h"
#include "Common/IniFile.h"
#include "Common/FileUtil.h"
#include "Common/GL/GLContext.h"
#include "Common/Logging/LogManager.h"
#include "Common/ParallelFor.h"
#include "Common/Thread.h"
#include "Common/scmrev.h"
#include "Common/Version.h"
#include "Core/ActionReplay.h"
#include "Core/BootManager.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SYSCONFSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
#include "Core/HW/CPU.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/VideoInterface.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/IOS/USB/Emulated/Microphone.h"
#include "Core/PatchEngine.h"
#include "Core/State.h"
#include "Core/System.h"
#include "DolphinLibretro/Audio.h"
#include "DolphinLibretro/Input.h"
#include "DolphinLibretro/Common/Options.h"
#include "DolphinLibretro/Video.h"
#include "DolphinLibretro/VR.h"
#include "VideoBackends/OGL/OGLTexture.h"
#include "VideoBackends/OGL/OGLGfx.h"
#include "VideoCommon/AsyncRequests.h"
#include "VideoCommon/Fifo.h"
#include "VideoCommon/Present.h"
#include "VideoCommon/VertexManagerBase.h"
#include "DolphinLibretro/Log.h"
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
#include "VideoCommon/Widescreen.h"
#include "Core/Boot/Boot.h"
#ifdef PERF_TEST
static struct retro_perf_callback perf_cb;
#define RETRO_PERFORMANCE_INIT(name) \
retro_perf_tick_t current_ticks; \
static struct retro_perf_counter name = {#name}; \
if (!name.registered) \
perf_cb.perf_register(&(name)); \
current_ticks = name.total
#define RETRO_PERFORMANCE_START(name) perf_cb.perf_start(&(name))
#define RETRO_PERFORMANCE_STOP(name) \
perf_cb.perf_stop(&(name)); \
current_ticks = name.total - current_ticks;
#else
#define RETRO_PERFORMANCE_INIT(name)
#define RETRO_PERFORMANCE_START(name)
#define RETRO_PERFORMANCE_STOP(name)
#endif
namespace Libretro
{
extern retro_environment_t environ_cb;
static bool widescreen;
// The aspect ratio last reported to the frontend (FrameAspect)
static float frame_aspect;
// Whether the geometry last reported to the frontend was sized for VR
static bool vr_frame;
double g_core_refresh_rate{0};
// Reset per game (see retro_run): the first refresh-rate settle after boot must not
// trigger a mode-family reinit, only a real in-game 50<->60 switch should.
static bool s_refresh_rate_settled = false;
extern void reload_cheats_from_ini();
extern unsigned msg_interface_version;
// Dolphin stretches its picture over the frame and the frontend gets the
// Aspect Ratio mode's ratio, so the picture is not scaled twice; Stretch and
// VR give 4:3 or 16:9 by whether the game runs widescreen.
static float FrameAspect()
{
const auto aspect_mode = static_cast<AspectMode>(Options::GetCached<int>(
Options::gfx_settings::ASPECT_RATIO, static_cast<int>(AspectMode::Stretch)));
if (aspect_mode == AspectMode::Stretch || VR::IsEnabled() || !g_presenter || !g_widescreen)
return widescreen ? 16.0f / 9.0f : 4.0f / 3.0f;
const float aspect = g_presenter->CalculateDrawAspectRatio(aspect_mode, false);
return aspect > 0.0f ? aspect : (widescreen ? 16.0f / 9.0f : 4.0f / 3.0f);
}
static void ApplyAspectMode()
{
if (VR::IsEnabled())
VR::ApplyConfig();
else
Config::SetCurrent(Config::GFX_ASPECT_RATIO, AspectMode::Stretch);
}
void FinishFieldInFlight()
{
Core::System& system = Core::System::GetInstance();
/* without the video backend, after the context is gone, there is no
* GPU to run */
if (!system.IsDualCoreMode() || !Core::IsRunning(system) || !g_vertex_manager)
return;
if (Libretro::Video::GpuThreadRunning())
{
/* the GPU thread finishes the field; parked then, until
* ResumeAfterField, so this thread has the video state */
while (Core::IsFrameStepping() && Core::GetState(system) == Core::State::Running)
Libretro::Video::GpuThreadWaitField((int64_t)cpu_features_get_time_usec() + 10000);
Libretro::Video::GpuThreadPark();
return;
}
/* stepping is what the end of a field leaves; a field's end already
* asked for stops the loop at once, so it is run until the CPU thread
* has really stopped */
while (Core::IsFrameStepping() && Core::GetState(system) == Core::State::Running)
system.GetFifo().RunGpuLoop(0);
}
void ResumeAfterField()
{
Libretro::Video::GpuThreadUnpark();
}
} // namespace Libretro
extern "C" {
void retro_set_environment(retro_environment_t cb)
{
Libretro::environ_cb = cb;
#ifdef PERF_TEST
environ_cb(RETRO_ENVIRONMENT_GET_PERF_INTERFACE, &perf_cb);
#endif
static const struct retro_subsystem_memory_info gbp_gba_memory[] = {
{"srm", RETRO_MEMORY_SAVE_RAM},
};
static const struct retro_subsystem_rom_info gbp_roms[] = {
{"GBA ROM", "gba|gbc|gb|zip|7z",
true,
true,
false,
gbp_gba_memory, 1},
{"GameCube Disc", "rvz|gcm|iso|ciso|wbfs|gcz|tgc|m3u",
true,
false,
true,
nullptr, 0},
};
static const struct retro_subsystem_info subsystems[] = {
{
"Game Boy Player",
"GBP",
gbp_roms, 2,
Libretro::g_gbplayer_subsystem_id
},
{nullptr, nullptr, nullptr, 0, 0}};
cb(RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO, (void*)subsystems);
// Declared before any game loads, so a frontend can list them without one
Libretro::Options::SetVariables();
}
void retro_init(void)
{
uint64_t serialization_quirks = RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE;
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS, &serialization_quirks);
enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
Libretro::environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION,
&Libretro::msg_interface_version);
}
void retro_deinit(void)
{
Libretro::g_emuthread_launched = false;
parallel_for_deinit();
#ifdef PERF_TEST
perf_cb.perf_log();
#endif
}
void retro_get_system_info(retro_system_info* info)
{
// Build a semver string from SCM_DESC_STR (e.g. "2512-226" -> "2512.0.226")
// with the short git hash as build metadata (e.g. "+13192dec3d")
static std::string version_str = []() {
std::string desc = SCM_DESC_STR;
std::string major, commits;
auto dash = desc.find('-');
if (dash != std::string::npos)
{
major = desc.substr(0, dash);
auto dash2 = desc.find('-', dash + 1);
commits = (dash2 != std::string::npos)
? desc.substr(dash + 1, dash2 - dash - 1)
: desc.substr(dash + 1);
}
else
{
major = desc;
commits = "0";
}
std::string hash = SCM_REV_STR;
if (hash.size() > 10)
hash = hash.substr(0, 10);
return major + ".0." + commits + "+" + hash;
}();
info->need_fullpath = true;
info->valid_extensions = "elf|dol|gcm|iso|tgc|wbfs|ciso|gcz|wad|wia|rvz|m3u";
info->library_version = version_str.c_str();
info->library_name = "ParaLLEl GX1";
info->block_extract = true;
}
void retro_get_system_av_info(retro_system_av_info* info)
{
int efbScale = Libretro::Options::GetCached<int>(
Libretro::Options::gfx_settings::EFB_SCALE);
int base_height = Libretro::Video::GetAdjustedBaseHeight();
// Fixed max (>= any runtime height) so base can grow via SET_GEOMETRY alone.
int max_height = base_height > 576 ? base_height : 576;
info->geometry.base_width = Libretro::Video::GetAdjustedBaseWidth() * efbScale;
info->geometry.base_height = base_height * efbScale;
info->geometry.max_width = Libretro::Video::GetAdjustedBaseWidth() * efbScale;
info->geometry.max_height = max_height * efbScale;
if (g_widescreen)
Libretro::widescreen = g_widescreen->IsGameWidescreen() || g_Config.bWidescreenHack;
else if (Core::System::GetInstance().IsWii())
Libretro::widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
info->geometry.aspect_ratio = Libretro::frame_aspect = Libretro::FrameAspect();
// The packed frame holds both eyes side by side
Libretro::vr_frame = Libretro::VR::IsEnabled();
if (Libretro::vr_frame)
info->geometry.aspect_ratio *= 2.0;
Core::System& system = Core::System::GetInstance();
double fps = system.GetVideoInterface().GetTargetRefreshRate();
if (fps <= 0.0)
fps = (retro_get_region() == RETRO_REGION_NTSC) ? (60.0 / 1.001) : 50.0;
info->timing.fps = fps;
info->timing.sample_rate = Libretro::Audio::GetSampleRate();
}
void retro_reset(void)
{
Core::System::GetInstance().GetProcessorInterface().ResetButton_Tap();
}
void retro_run(void)
{
// One retro_run is one frame of the core's timebase, as long as the frontend was told.
const double frame_hz = Libretro::g_core_refresh_rate > 1.0 ? Libretro::g_core_refresh_rate : 60.0;
frame_clock_advance(static_cast<unsigned>(std::lround(1e6 / frame_hz)));
Libretro::Input::InitSensors();
Libretro::Options::CheckForUpdatedVariables();
if (Libretro::Options::IsUpdated(Libretro::Options::main_interface::LOG_LEVEL))
{
#if defined(_DEBUG)
Common::Log::LogManager::GetInstance()->SetConfigLogLevel(Common::Log::LogLevel::LDEBUG);
#else
Common::Log::LogManager::GetInstance()->SetConfigLogLevel(
static_cast<Common::Log::LogLevel>(
Libretro::Options::GetCached<int>(
Libretro::Options::main_interface::LOG_LEVEL, static_cast<int>(Common::Log::LogLevel::LINFO))));
#endif
}
if (Libretro::Options::IsUpdated(Libretro::Options::core::CPU_CLOCK_RATE))
{
double cpuClock = Libretro::Options::GetCached<double>(
Libretro::Options::core::CPU_CLOCK_RATE);
Config::SetCurrent(Config::MAIN_OVERCLOCK, cpuClock);
Config::SetCurrent(Config::MAIN_OVERCLOCK_ENABLE, cpuClock != 1.0);
}
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::WIDESCREEN_HACK))
{
g_Config.bWidescreenHack = Libretro::Options::GetCached<bool>(
Libretro::Options::gfx_settings::WIDESCREEN_HACK);
}
// Crop to 4:3 when advertising 480-line geometry (NTSC / PAL60), tracked per frame.
g_Config.bCropToAspectRatio = (Libretro::Video::GetAdjustedBaseHeight() == 480);
Libretro::Input::Update();
Core::System& system = Core::System::GetInstance();
if (Core::GetState(system) == Core::State::Starting &&
!Libretro::g_emuthread_launched)
{
WindowSystemInfo wsi(WindowSystemType::Libretro, nullptr, nullptr, nullptr);
/* The video backend did not come up (the log says why): booting would
* draw through it at once, so stop here instead. */
if (!g_vertex_manager)
{
ERROR_LOG_FMT(BOOT, "The video backend failed to initialize; not booting.");
Libretro::Log::DoLogFrontEnd(RETRO_LOG_ERROR,
"The video driver failed to initialize; see the log.", 5000);
Libretro::environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
return;
}
if (system.IsDualCoreMode())
{
Core::StartEmuThread(system, std::move(Core::g_boot_params), wsi);
/* until the CPU thread has reached its run loop, this thread runs
* the GPU: what the CPU thread does first may wait on it, as the
* FIFO player clearing the EFB does */
Libretro::Video::D3D11Lock();
while (!system.GetCPU().HasCPURunStateBeenReached())
system.GetFifo().RunGpuLoop(5000);
Libretro::Video::D3D11Unlock();
}
else
Core::EmuThread(system, std::move(Core::g_boot_params), wsi);
Libretro::g_emuthread_launched = true;
if(Config::Get(Config::MAIN_GFX_BACKEND) == "Software Renderer")
{
g_gfx.reset();
g_gfx = std::make_unique<Libretro::Video::SWGfx>();
}
else if (Config::Get(Config::MAIN_GFX_BACKEND) == "Null")
{
g_gfx.reset();
g_gfx = std::make_unique<Libretro::Video::NullGfx>();
}
Libretro::g_core_refresh_rate = system.GetVideoInterface().GetTargetRefreshRate();
Libretro::s_refresh_rate_settled = false;
// Expose GameCube and Wii memory maps to libretro
{
auto& memory = system.GetMemory();
struct retro_memory_descriptor descs[2] = {};
unsigned num_descs = 0;
descs[num_descs].ptr = memory.GetRAM();
descs[num_descs].start = 0x80000000;
descs[num_descs].len = memory.GetRamSizeReal();
descs[num_descs].flags = RETRO_MEMDESC_BIGENDIAN | RETRO_MEMORY_SYSTEM_RAM;
descs[num_descs].addrspace = "MEM1";
num_descs++;
if (system.IsWii())
{
descs[num_descs].ptr = memory.GetEXRAM();
descs[num_descs].start = 0x90000000;
descs[num_descs].len = memory.GetExRamSizeReal();
descs[num_descs].flags = RETRO_MEMDESC_BIGENDIAN | RETRO_MEMORY_SYSTEM_RAM;
descs[num_descs].addrspace = "MEM2";
num_descs++;
}
struct retro_memory_map mmap = {descs, num_descs};
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmap);
}
}
if(!Libretro::g_emuthread_launched)
{
DEBUG_LOG_FMT(BOOT, "retro_run() - waiting for g_emuthread_launched");
return;
}
if (g_gfx && Config::Get(Config::MAIN_GFX_BACKEND) == "OGL")
{
static_cast<OGL::OGLGfx*>(g_gfx.get())
->SetSystemFrameBuffer((GLuint)Libretro::Video::hw_render.get_current_framebuffer());
}
// Turning VR on or off doubles or halves the frame width, so resize it as for EFB scale.
bool vr_resized = false;
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::VR_MODE))
{
const bool vr_enabled = Libretro::VR::IsEnabled();
if (vr_enabled || Libretro::vr_frame)
Libretro::VR::ApplyConfig();
vr_resized = vr_enabled != Libretro::vr_frame;
}
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::EFB_SCALE) || vr_resized)
{
g_Config.iEFBScale = Libretro::Options::GetCached<int>(
Libretro::Options::gfx_settings::EFB_SCALE);
unsigned cmd = RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO;
if (Libretro::Video::hw_render.context_type == RETRO_HW_CONTEXT_D3D11 ||
Libretro::Video::hw_render.context_type == RETRO_HW_CONTEXT_D3D12)
cmd = RETRO_ENVIRONMENT_SET_GEOMETRY;
retro_system_av_info info;
retro_get_system_av_info(&info);
Libretro::environ_cb(cmd, &info);
}
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::CROP_OVERSCAN))
{
retro_system_av_info info;
retro_get_system_av_info(&info);
// SET_GEOMETRY, not SET_SYSTEM_AV_INFO: the reinit would drop KMS to 240p.
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
}
if ((g_widescreen &&
Libretro::widescreen != (g_widescreen->IsGameWidescreen() || g_Config.bWidescreenHack)) ||
std::fabs(Libretro::FrameAspect() - Libretro::frame_aspect) > 0.001f)
{
retro_system_av_info info;
retro_get_system_av_info(&info);
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
}
// target refresh rate has changed - e.g. user has chosen 60 Hz for a PAL game
double new_rate = system.GetVideoInterface().GetTargetRefreshRate();
if (round(Libretro::g_core_refresh_rate * 1e6) != round(new_rate * 1e6))
{
const double old_rate = Libretro::g_core_refresh_rate;
NOTICE_LOG_FMT(VIDEO, "Target refresh rate changed: {:.4f} Hz -> {:.4f} Hz",
old_rate, new_rate);
Libretro::g_core_refresh_rate = new_rate;
retro_system_av_info info;
retro_get_system_av_info(&info);
// 50<->60 switch needs a new CRT mode family: only SET_SYSTEM_AV_INFO carries
// the fps switchres uses, then SET_GEOMETRY rebuilds the surface its reinit drops.
// First settle after boot keeps the initial mode, so geometry-only (no reinit flash).
const bool crosses_50_60 =
Libretro::s_refresh_rate_settled && ((old_rate > 55.0) != (new_rate > 55.0));
Libretro::s_refresh_rate_settled = true;
if (crosses_50_60)
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
}
WiimoteUpdateFlags flags;
if (system.IsWii())
{
flags.irMode = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_MODE);
flags.irOffset = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_OFFSET);
flags.irYaw = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_YAW);
flags.irPitch = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_PITCH);
flags.irDeadzone = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_DEADZONE);
flags.irModifier = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_MODIFIER);
flags.swingModifier = Libretro::Options::IsUpdated(Libretro::Options::wiimote::SWING_MODIFIER);
flags.swingAngle = Libretro::Options::IsUpdated(Libretro::Options::wiimote::SWING_ANGLE);
flags.sideways = Libretro::Options::IsUpdated(Libretro::Options::wiimote::HOTKEY_SIDEWAYS_TOGGLE);
flags.upright = Libretro::Options::IsUpdated(Libretro::Options::wiimote::HOTKEY_UPRIGHT_TOGGLE);
flags.irPassthrough = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_PASSTHROUGH);
}
flags.rumble = Libretro::Options::IsUpdated(Libretro::Options::sysconf::ENABLE_RUMBLE);
flags.gcMicBtn = Libretro::Options::IsUpdated(Libretro::Options::sysconf_gc::HOTKEY_ACTIVATE_MICROPHONE);
if (flags.any())
Libretro::Input::ResetControllers(flags);
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::ASPECT_RATIO))
{
// VR sets the aspect ratio together with its stereoscopy settings
Libretro::ApplyAspectMode();
retro_system_av_info info;
retro_get_system_av_info(&info);
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
}
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_TO_TEXTURE))
Config::SetBase(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_TO_TEXTURE, true));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_ENABLE))
Config::SetBase(Config::GFX_HACK_EFB_ACCESS_ENABLE, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_ACCESS_ENABLE, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_DEFER_INVALIDATION))
Config::SetBase(Config::GFX_HACK_EFB_DEFER_INVALIDATION, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_ACCESS_DEFER_INVALIDATION, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_TILE_SIZE))
Config::SetBase(Config::GFX_HACK_EFB_ACCESS_TILE_SIZE, Libretro::Options::GetCached<int>(Libretro::Options::gfx_hacks::EFB_ACCESS_TILE_SIZE, 64));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::BBOX_ENABLED))
Config::SetBase(Config::GFX_HACK_BBOX_ENABLE, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::BBOX_ENABLED, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::XFB_TO_TEXTURE_ENABLE))
Config::SetBase(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::XFB_TO_TEXTURE_ENABLE, true));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_TO_VRAM))
Config::SetBase(Config::GFX_HACK_DISABLE_COPY_TO_VRAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_TO_VRAM, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::DEFER_EFB_COPIES))
Config::SetBase(Config::GFX_HACK_DEFER_EFB_COPIES, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::DEFER_EFB_COPIES, true));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::IMMEDIATE_XFB))
Config::SetBase(Config::GFX_HACK_IMMEDIATE_XFB, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::IMMEDIATE_XFB, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::SKIP_DUPE_FRAMES))
Config::SetBase(Config::GFX_HACK_SKIP_DUPLICATE_XFBS, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::SKIP_DUPE_FRAMES, true));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_SCALED_COPY))
Config::SetBase(Config::GFX_HACK_COPY_EFB_SCALED, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_SCALED_COPY, true));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_EMULATE_FORMAT_CHANGES))
Config::SetBase(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_EMULATE_FORMAT_CHANGES, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::VERTEX_ROUNDING))
Config::SetBase(Config::GFX_HACK_VERTEX_ROUNDING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::VERTEX_ROUNDING, false));
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::FAST_TEXTURE_SAMPLING))
Config::SetBase(Config::GFX_HACK_FAST_TEXTURE_SAMPLING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::FAST_TEXTURE_SAMPLING, true));
#ifdef __APPLE__
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::NO_MIPMAPPING))
Config::SetBase(Config::GFX_HACK_NO_MIPMAPPING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::NO_MIPMAPPING, false));
#endif
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::TEXTURE_CACHE_ACCURACY))
Config::SetBase(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES, Libretro::Options::GetCached<int>(Libretro::Options::gfx_settings::TEXTURE_CACHE_ACCURACY, 128));
if (Libretro::Options::IsUpdated(Libretro::Options::sysconf::WII_SPEAK_MUTED))
Config::SetCurrent(Config::MAIN_WII_SPEAK_MUTED,
Libretro::Options::GetCached<bool>(Libretro::Options::sysconf::WII_SPEAK_MUTED));
const bool wiimoteDeviceUpdated =
Libretro::Options::IsUpdated(Libretro::Options::wiimote::DEFAULT_DEVICE);
if (Libretro::Options::IsUpdated(Libretro::Options::sysconf::WIIMOTE_CONTINUOUS_SCANNING) ||
wiimoteDeviceUpdated)
{
Config::SetCurrent(Config::MAIN_WIIMOTE_CONTINUOUS_SCANNING,
Libretro::Options::GetCached<bool>(Libretro::Options::sysconf::WIIMOTE_CONTINUOUS_SCANNING) ||
Libretro::Options::GetCached<std::string>(Libretro::Options::wiimote::DEFAULT_DEVICE) == "real");
WiimoteReal::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
}
if (wiimoteDeviceUpdated)
Libretro::Input::ResetControllers(WiimoteUpdateFlags{});
if (Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
Libretro::Input::BluetoothPassthroughBind();
Libretro::VR::Update(Libretro::widescreen ? 16.0f / 9.0f : 4.0f / 3.0f);
RETRO_PERFORMANCE_INIT(dolphin_main_func);
RETRO_PERFORMANCE_START(dolphin_main_func);
if (system.IsDualCoreMode())
{
/* two of the frontend's frames, and never more than 40 ms: a field
* held up on the CPU thread, by a disc read or anything else, leaves
* this run to report a repeat and the rest of the field to the next */
s64 budget_us = (s64)(2000000.0 / frame_hz);
if (budget_us > 40000)
budget_us = 40000;
if (Libretro::Video::GpuThreadWanted())
{
/* the GPU thread runs the field; this thread waits for it no
* longer than the budget, and EndFrame hands over what is done */
Libretro::Video::GpuThreadEnsure();
Core::DoFrameStep(system);
Libretro::Video::GpuThreadWaitField((int64_t)cpu_features_get_time_usec() + budget_us);
}
else
{
Libretro::Video::D3D11Lock();
VideoCommon::g_frontend_takes_frames = true;
Core::DoFrameStep(system);
system.GetFifo().RunGpuLoop(budget_us);
VideoCommon::g_frontend_takes_frames = false;
Libretro::Video::D3D11Unlock();
}
}
else
{
Libretro::Video::D3D11Lock();
VideoCommon::g_frontend_takes_frames = true;
system.GetCPU().RunSingleFrame();
VideoCommon::g_frontend_takes_frames = false;
Libretro::Video::D3D11Unlock();
}
RETRO_PERFORMANCE_STOP(dolphin_main_func);
Libretro::Video::EndFrame();
if (Libretro::Input::g_has_microphone_support)
poll_microphone();
// What the frame produced for the frontend, handed over from its own thread: the rumble
// asked for and the field's audio.
Libretro::Input::FlushOutputs();
Libretro::Audio::PushFields();
}
size_t retro_serialize_size(void)
{
size_t size = 0;
Core::System& system = Core::System::GetInstance();
if (!Core::IsRunning(system))
return 0;
Libretro::FinishFieldInFlight();
AsyncRequests* ar = AsyncRequests::GetInstance();
if (system.IsDualCoreMode())
ar->SetPassthrough(true);
Libretro::Video::D3D11Lock();
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
PointerWrap p((u8**)&size, sizeof(size_t), PointerWrap::Mode::Measure);
State::DoState(Core::System::GetInstance(), p);
}, true); // wait = true
Libretro::Video::D3D11Unlock();
if (system.IsDualCoreMode())
ar->SetPassthrough(false);
Libretro::ResumeAfterField();
return size;
}
bool retro_serialize(void* data, size_t size)
{
Core::System& system = Core::System::GetInstance();
if (!Core::IsRunning(system))
return false;
Libretro::FinishFieldInFlight();
AsyncRequests* ar = AsyncRequests::GetInstance();
if (system.IsDualCoreMode())
ar->SetPassthrough(true);
const bool was_cpu = Core::IsCPUThread();
if (!was_cpu)
Core::DeclareAsCPUThread();
Libretro::Video::D3D11Lock();
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
PointerWrap p((u8**)&data, size, PointerWrap::Mode::Write);
State::DoState(Core::System::GetInstance(), p);
}, true);
Libretro::Video::D3D11Unlock();
if (!was_cpu)
Core::UndeclareAsCPUThread();
if (system.IsDualCoreMode())
ar->SetPassthrough(false);
Libretro::ResumeAfterField();
return true;
}
bool retro_unserialize(const void* data, size_t size)
{
Core::System& system = Core::System::GetInstance();
if (!Core::IsRunning(system))
return false;
Libretro::FinishFieldInFlight();
AsyncRequests* ar = AsyncRequests::GetInstance();
if (system.IsDualCoreMode())
ar->SetPassthrough(true);
const bool was_cpu = Core::IsCPUThread();
if (!was_cpu)
Core::DeclareAsCPUThread();
Libretro::Video::D3D11Lock();
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
PointerWrap p((u8**)&data, size, PointerWrap::Mode::Read);
State::DoState(Core::System::GetInstance(), p);
}, true);
Libretro::Video::D3D11Unlock();
if (!was_cpu)
Core::UndeclareAsCPUThread();
if (system.IsDualCoreMode())
ar->SetPassthrough(false);
Libretro::ResumeAfterField();
return true;
}
unsigned retro_get_region()
{
Core::System& system = Core::System::GetInstance();
if (DiscIO::IsNTSC(SConfig::GetInstance().m_region) ||
(Core::System::GetInstance().IsWii() && Config::Get(Config::SYSCONF_PAL60)) ||
system.IsTriforce())
return RETRO_REGION_NTSC;
return RETRO_REGION_PAL;
}
unsigned retro_api_version()
{
return RETRO_API_VERSION;
}
size_t retro_get_memory_size(unsigned id)
{
auto& memory = Core::System::GetInstance().GetMemory();
if (id == RETRO_MEMORY_SYSTEM_RAM)
return memory.GetRamSizeReal();
return 0;
}
void* retro_get_memory_data(unsigned id)
{
auto& memory = Core::System::GetInstance().GetMemory();
if (id == RETRO_MEMORY_SYSTEM_RAM)
return memory.GetRAM();
return NULL;
}
static void enable_cheat_by_code(bool enabled, const char* code)
{
std::string incoming(code);
bool found = false;
// Match Action Replay codes
for (auto& c : Libretro::g_ar_codes)
{
std::string serialized;
serialized.reserve(64);
for (const auto& op : c.ops)
{
if (!serialized.empty())
serialized += "+";
serialized += fmt::format("{:08X} {:08X}", op.cmd_addr, op.value);
}
if (serialized == incoming)
{
c.enabled = enabled;
found = true;
break;
}
}
// Match Gecko codes only if not already matched
if (!found)
{
for (auto& c : Libretro::g_gecko_codes)
{
std::string serialized;
serialized.reserve(64);
for (const auto& line : c.codes)
{
if (!serialized.empty())
serialized += "+";
serialized += line.original_line;
}
if (serialized == incoming)
{
c.enabled = enabled;
found = true;
break;
}
}
}
}
// reset cheats to off
void retro_cheat_reset(void)
{
const bool importCheats = Libretro::GetOption<bool>(Libretro::Options::retroarch_core::CHEATS_IMPORT, true);
if (!Config::AreCheatsEnabled() || !importCheats)
return;
Libretro::FinishFieldInFlight();
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
PatchEngine::Shutdown();
}, true);
Libretro::ResumeAfterField();
Libretro::reload_cheats_from_ini();
}
// Enable/disable a cheat
void retro_cheat_set(unsigned index, bool enabled, const char *code)
{
if (!Config::AreCheatsEnabled())
{
Core::DisplayMessage("You cannot apply any cheats until you have enabled the following option:\n"
"Core > Internal Cheats - set this to ON");
return;
}
const bool importCheats = Libretro::GetOption<bool>(Libretro::Options::retroarch_core::CHEATS_IMPORT, true);
if (!importCheats)
{
Core::DisplayMessage("You cannot apply any cheats until you enabled the following option:\n"
"Core > Automatically Import Cheats into RetroArch - set this to ON");
return;
}
const std::string& game_id = SConfig::GetInstance().GetGameID();
const u16 game_revision = SConfig::GetInstance().GetRevision();
// Look up by code as index is unreliable
enable_cheat_by_code(enabled, code);
Libretro::FinishFieldInFlight();
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
// Apply Action Replay
ActionReplay::ApplyCodes(Libretro::g_ar_codes, game_id, game_revision);
// Apply Gecko
Gecko::SetActiveCodes(Libretro::g_gecko_codes, game_id, game_revision);
}, true);
Libretro::ResumeAfterField();
}
} // extern "C"