Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5091012cfb | |||
| 87a769c461 |
@@ -9,11 +9,9 @@
|
||||
#include "Common/Common.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
// This shouldn't be a global, at least not here.
|
||||
extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
std::unique_ptr<SoundStream> g_sound_stream;
|
||||
|
||||
static bool s_audio_dump_start = false;
|
||||
@@ -24,42 +22,34 @@ namespace AudioCommon
|
||||
static const int AUDIO_VOLUME_MIN = 0;
|
||||
static const int AUDIO_VOLUME_MAX = 100;
|
||||
|
||||
void InitSoundStream(Core::System& system)
|
||||
void InitSoundStream()
|
||||
{
|
||||
g_sound_stream = std::make_unique<OpenEmuAudioStream>();
|
||||
|
||||
if (!g_sound_stream->Init())
|
||||
{
|
||||
WARN_LOG_FMT(AUDIO, "Could not initialize backend");
|
||||
WARN_LOG(AUDIO, "Could not initialize backend");
|
||||
g_sound_stream = std::make_unique<NullSound>();
|
||||
}
|
||||
|
||||
UpdateSoundStream(system);
|
||||
SetSoundStreamRunning(system, true);
|
||||
UpdateSoundStream();
|
||||
SetSoundStreamRunning(true);
|
||||
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
StartAudioDump();
|
||||
}
|
||||
|
||||
void PostInitSoundStream(Core::System& system)
|
||||
void ShutdownSoundStream()
|
||||
{
|
||||
// This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices)
|
||||
// where input sample rates are set
|
||||
UpdateSoundStream(system);
|
||||
SetSoundStreamRunning(system, true);
|
||||
INFO_LOG(AUDIO, "Shutting down sound stream");
|
||||
|
||||
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !s_audio_dump_start)
|
||||
StartAudioDump(system);
|
||||
}
|
||||
|
||||
void ShutdownSoundStream(Core::System& system)
|
||||
{
|
||||
INFO_LOG_FMT(AUDIO, "Shutting down sound stream");
|
||||
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
|
||||
if (Config::Get(Config::MAIN_DUMP_AUDIO) && s_audio_dump_start)
|
||||
StopAudioDump(system);
|
||||
|
||||
SetSoundStreamRunning(system, false);
|
||||
SetSoundStreamRunning(false);
|
||||
g_sound_stream.reset();
|
||||
|
||||
INFO_LOG_FMT(AUDIO, "Done shutting down sound stream");
|
||||
INFO_LOG(AUDIO, "Done shutting down sound stream");
|
||||
}
|
||||
|
||||
std::string GetDefaultSoundBackend()
|
||||
@@ -98,16 +88,16 @@ DPL2Quality GetDefaultDPL2Quality()
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateSoundStream(Core::System& system)
|
||||
void UpdateSoundStream()
|
||||
{
|
||||
if (g_sound_stream)
|
||||
{
|
||||
int volume = Config::Get(Config::MAIN_AUDIO_MUTED) ? 0 : Config::Get(Config::MAIN_AUDIO_VOLUME);
|
||||
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
|
||||
g_sound_stream->SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void SetSoundStreamRunning(Core::System& system, bool running)
|
||||
void SetSoundStreamRunning(bool running)
|
||||
{
|
||||
if (!g_sound_stream)
|
||||
return;
|
||||
@@ -119,20 +109,20 @@ DPL2Quality GetDefaultDPL2Quality()
|
||||
if (g_sound_stream->SetRunning(running))
|
||||
return;
|
||||
if (running)
|
||||
ERROR_LOG_FMT(AUDIO, "Error starting stream.");
|
||||
ERROR_LOG(AUDIO, "Error starting stream.");
|
||||
else
|
||||
ERROR_LOG_FMT(AUDIO, "Error stopping stream.");
|
||||
ERROR_LOG(AUDIO, "Error stopping stream.");
|
||||
}
|
||||
|
||||
void SendAIBuffer(Core::System& system, const short* samples, unsigned int num_samples)
|
||||
void SendAIBuffer(const short* samples, unsigned int num_samples)
|
||||
{
|
||||
if (!g_sound_stream)
|
||||
return;
|
||||
|
||||
if (Config::Get(Config::MAIN_DUMP_AUDIO) && !s_audio_dump_start)
|
||||
StartAudioDump(system);
|
||||
else if (!Config::Get(Config::MAIN_DUMP_AUDIO) && s_audio_dump_start)
|
||||
StopAudioDump(system);
|
||||
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
|
||||
StartAudioDump();
|
||||
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
|
||||
StopAudioDump();
|
||||
|
||||
Mixer* pMixer = g_sound_stream->GetMixer();
|
||||
|
||||
@@ -140,9 +130,11 @@ DPL2Quality GetDefaultDPL2Quality()
|
||||
{
|
||||
pMixer->PushSamples(samples, num_samples);
|
||||
}
|
||||
|
||||
g_sound_stream->Update();
|
||||
}
|
||||
|
||||
void StartAudioDump(Core::System& system)
|
||||
void StartAudioDump()
|
||||
{
|
||||
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
|
||||
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
|
||||
@@ -153,7 +145,7 @@ DPL2Quality GetDefaultDPL2Quality()
|
||||
s_audio_dump_start = true;
|
||||
}
|
||||
|
||||
void StopAudioDump(Core::System& system)
|
||||
void StopAudioDump()
|
||||
{
|
||||
if (!g_sound_stream)
|
||||
return;
|
||||
@@ -162,32 +154,30 @@ DPL2Quality GetDefaultDPL2Quality()
|
||||
s_audio_dump_start = false;
|
||||
}
|
||||
|
||||
void IncreaseVolume(Core::System& system, unsigned short offset)
|
||||
void IncreaseVolume(unsigned short offset)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, false);
|
||||
int currentVolume = Config::Get(Config::MAIN_AUDIO_VOLUME);
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume += offset;
|
||||
if (currentVolume > AUDIO_VOLUME_MAX)
|
||||
currentVolume = AUDIO_VOLUME_MAX;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, currentVolume);
|
||||
UpdateSoundStream(system);
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void DecreaseVolume(Core::System& system, unsigned short offset)
|
||||
void DecreaseVolume(unsigned short offset)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, false);
|
||||
int currentVolume = Config::Get(Config::MAIN_AUDIO_VOLUME);
|
||||
SConfig::GetInstance().m_IsMuted = false;
|
||||
int& currentVolume = SConfig::GetInstance().m_Volume;
|
||||
currentVolume -= offset;
|
||||
if (currentVolume < AUDIO_VOLUME_MIN)
|
||||
currentVolume = AUDIO_VOLUME_MIN;
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, currentVolume);
|
||||
UpdateSoundStream(system);
|
||||
UpdateSoundStream();
|
||||
}
|
||||
|
||||
void ToggleMuteVolume(Core::System& system)
|
||||
void ToggleMuteVolume()
|
||||
{
|
||||
bool isMuted = Config::Get(Config::MAIN_AUDIO_MUTED);
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_MUTED, !isMuted);
|
||||
UpdateSoundStream(system);
|
||||
bool& isMuted = SConfig::GetInstance().m_IsMuted;
|
||||
isMuted = !isMuted;
|
||||
UpdateSoundStream();
|
||||
}
|
||||
} // namespace AudioCommon
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2022, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "../../dolphin/Source/Core/Common/x64CPUDetect.cpp"
|
||||
#elif defined(__arm64__)
|
||||
#include "../../dolphin/Source/Core/Common/ArmCPUDetect.cpp"
|
||||
#endif
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2022, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "../../dolphin/Source/Core/Common/x64FPURoundMode.cpp"
|
||||
#endif
|
||||
Regular → Executable
+478
-401
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
#include "AudioCommon/AudioCommon.h"
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CDUtils.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/Config.h"
|
||||
@@ -27,13 +28,13 @@
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/scmrev.h"
|
||||
|
||||
#include "Core/Analytics.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/SYSCONFSettings.h"
|
||||
#include "Core/ConfigLoaders/GameConfigLoader.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/FifoPlayer/FifoDataFile.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HW/DVD/DVDInterface.h"
|
||||
@@ -81,13 +82,538 @@ SConfig::~SConfig()
|
||||
void SConfig::SaveSettings()
|
||||
{
|
||||
NOTICE_LOG_FMT(BOOT, "Saving settings to {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
IniFile ini;
|
||||
ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX)); // load first to not kill unknown stuff
|
||||
|
||||
SaveGeneralSettings(ini);
|
||||
SaveInterfaceSettings(ini);
|
||||
SaveGameListSettings(ini);
|
||||
SaveCoreSettings(ini);
|
||||
SaveMovieSettings(ini);
|
||||
SaveDSPSettings(ini);
|
||||
SaveInputSettings(ini);
|
||||
SaveFifoPlayerSettings(ini);
|
||||
SaveBluetoothPassthroughSettings(ini);
|
||||
SaveUSBPassthroughSettings(ini);
|
||||
SaveAutoUpdateSettings(ini);
|
||||
SaveJitDebugSettings(ini);
|
||||
|
||||
ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
|
||||
Config::Save();
|
||||
}
|
||||
|
||||
void SConfig::SaveGeneralSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* general = ini.GetOrCreateSection("General");
|
||||
|
||||
// General
|
||||
general->Set("ShowLag", m_ShowLag);
|
||||
general->Set("ShowFrameCount", m_ShowFrameCount);
|
||||
|
||||
// ISO folders
|
||||
// Clear removed folders
|
||||
int oldPaths;
|
||||
int numPaths = (int)m_ISOFolder.size();
|
||||
general->Get("ISOPaths", &oldPaths, 0);
|
||||
for (int i = numPaths; i < oldPaths; i++)
|
||||
{
|
||||
ini.DeleteKey("General", fmt::format("ISOPath{}", i));
|
||||
}
|
||||
|
||||
general->Set("ISOPaths", numPaths);
|
||||
for (int i = 0; i < numPaths; i++)
|
||||
{
|
||||
general->Set(fmt::format("ISOPath{}", i), m_ISOFolder[i]);
|
||||
}
|
||||
|
||||
general->Set("WirelessMac", m_WirelessMac);
|
||||
|
||||
#ifdef USE_GDBSTUB
|
||||
#ifndef _WIN32
|
||||
general->Set("GDBSocket", gdb_socket);
|
||||
#endif
|
||||
general->Set("GDBPort", iGDBPort);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SConfig::SaveInterfaceSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* interface = ini.GetOrCreateSection("Interface");
|
||||
|
||||
interface->Set("ConfirmStop", bConfirmStop);
|
||||
interface->Set("HideCursor", bHideCursor);
|
||||
interface->Set("LanguageCode", m_InterfaceLanguage);
|
||||
interface->Set("ExtendedFPSInfo", m_InterfaceExtendedFPSInfo);
|
||||
interface->Set("ShowActiveTitle", m_show_active_title);
|
||||
interface->Set("UseBuiltinTitleDatabase", m_use_builtin_title_database);
|
||||
interface->Set("ThemeName", theme_name);
|
||||
interface->Set("PauseOnFocusLost", m_PauseOnFocusLost);
|
||||
interface->Set("DebugModeEnabled", bEnableDebugging);
|
||||
}
|
||||
|
||||
void SConfig::SaveGameListSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* gamelist = ini.GetOrCreateSection("GameList");
|
||||
|
||||
gamelist->Set("ListDrives", m_ListDrives);
|
||||
gamelist->Set("ListWad", m_ListWad);
|
||||
gamelist->Set("ListElfDol", m_ListElfDol);
|
||||
gamelist->Set("ListWii", m_ListWii);
|
||||
gamelist->Set("ListGC", m_ListGC);
|
||||
gamelist->Set("ListJap", m_ListJap);
|
||||
gamelist->Set("ListPal", m_ListPal);
|
||||
gamelist->Set("ListUsa", m_ListUsa);
|
||||
gamelist->Set("ListAustralia", m_ListAustralia);
|
||||
gamelist->Set("ListFrance", m_ListFrance);
|
||||
gamelist->Set("ListGermany", m_ListGermany);
|
||||
gamelist->Set("ListItaly", m_ListItaly);
|
||||
gamelist->Set("ListKorea", m_ListKorea);
|
||||
gamelist->Set("ListNetherlands", m_ListNetherlands);
|
||||
gamelist->Set("ListRussia", m_ListRussia);
|
||||
gamelist->Set("ListSpain", m_ListSpain);
|
||||
gamelist->Set("ListTaiwan", m_ListTaiwan);
|
||||
gamelist->Set("ListWorld", m_ListWorld);
|
||||
gamelist->Set("ListUnknown", m_ListUnknown);
|
||||
gamelist->Set("ListSort", m_ListSort);
|
||||
gamelist->Set("ListSortSecondary", m_ListSort2);
|
||||
|
||||
gamelist->Set("ColumnPlatform", m_showSystemColumn);
|
||||
gamelist->Set("ColumnBanner", m_showBannerColumn);
|
||||
gamelist->Set("ColumnDescription", m_showDescriptionColumn);
|
||||
gamelist->Set("ColumnTitle", m_showTitleColumn);
|
||||
gamelist->Set("ColumnNotes", m_showMakerColumn);
|
||||
gamelist->Set("ColumnFileName", m_showFileNameColumn);
|
||||
gamelist->Set("ColumnFilePath", m_showFilePathColumn);
|
||||
gamelist->Set("ColumnID", m_showIDColumn);
|
||||
gamelist->Set("ColumnRegion", m_showRegionColumn);
|
||||
gamelist->Set("ColumnSize", m_showSizeColumn);
|
||||
gamelist->Set("ColumnFileFormat", m_showFileFormatColumn);
|
||||
gamelist->Set("ColumnBlockSize", m_showBlockSizeColumn);
|
||||
gamelist->Set("ColumnCompression", m_showCompressionColumn);
|
||||
gamelist->Set("ColumnTags", m_showTagsColumn);
|
||||
}
|
||||
|
||||
void SConfig::SaveCoreSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* core = ini.GetOrCreateSection("Core");
|
||||
|
||||
core->Set("SkipIPL", bHLE_BS2);
|
||||
core->Set("TimingVariance", iTimingVariance);
|
||||
core->Set("CPUCore", cpu_core);
|
||||
core->Set("Fastmem", bFastmem);
|
||||
core->Set("CPUThread", bCPUThread);
|
||||
core->Set("DSPHLE", bDSPHLE);
|
||||
core->Set("SyncOnSkipIdle", bSyncGPUOnSkipIdleHack);
|
||||
core->Set("SyncGPU", bSyncGPU);
|
||||
core->Set("SyncGpuMaxDistance", iSyncGpuMaxDistance);
|
||||
core->Set("SyncGpuMinDistance", iSyncGpuMinDistance);
|
||||
core->Set("SyncGpuOverclock", fSyncGpuOverclock);
|
||||
core->Set("FPRF", bFPRF);
|
||||
core->Set("AccurateNaNs", bAccurateNaNs);
|
||||
core->Set("EnableCheats", bEnableCheats);
|
||||
core->Set("SelectedLanguage", SelectedLanguage);
|
||||
core->Set("OverrideRegionSettings", bOverrideRegionSettings);
|
||||
core->Set("DPL2Decoder", bDPL2Decoder);
|
||||
core->Set("AudioLatency", iLatency);
|
||||
core->Set("AudioStretch", m_audio_stretch);
|
||||
core->Set("AudioStretchMaxLatency", m_audio_stretch_max_latency);
|
||||
core->Set("AgpCartAPath", m_strGbaCartA);
|
||||
core->Set("AgpCartBPath", m_strGbaCartB);
|
||||
core->Set("SlotA", m_EXIDevice[0]);
|
||||
core->Set("SlotB", m_EXIDevice[1]);
|
||||
core->Set("SerialPort1", m_EXIDevice[2]);
|
||||
core->Set("BBA_MAC", m_bba_mac);
|
||||
core->Set("BBA_XLINK_IP", m_bba_xlink_ip);
|
||||
core->Set("BBA_XLINK_CHAT_OSD", m_bba_xlink_chat_osd);
|
||||
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
|
||||
{
|
||||
core->Set(fmt::format("SIDevice{}", i), m_SIDevice[i]);
|
||||
core->Set(fmt::format("AdapterRumble{}", i), m_AdapterRumble[i]);
|
||||
core->Set(fmt::format("SimulateKonga{}", i), m_AdapterKonga[i]);
|
||||
}
|
||||
core->Set("WiiSDCard", m_WiiSDCard);
|
||||
core->Set("WiiKeyboard", m_WiiKeyboard);
|
||||
core->Set("WiimoteContinuousScanning", m_WiimoteContinuousScanning);
|
||||
core->Set("WiimoteEnableSpeaker", m_WiimoteEnableSpeaker);
|
||||
core->Set("WiimoteControllerInterface", connect_wiimotes_for_ciface);
|
||||
core->Set("RunCompareServer", bRunCompareServer);
|
||||
core->Set("RunCompareClient", bRunCompareClient);
|
||||
core->Set("MMU", bMMU);
|
||||
core->Set("EmulationSpeed", m_EmulationSpeed);
|
||||
core->Set("Overclock", m_OCFactor);
|
||||
core->Set("OverclockEnable", m_OCEnable);
|
||||
core->Set("GPUDeterminismMode", m_strGPUDeterminismMode);
|
||||
core->Set("PerfMapDir", m_perfDir);
|
||||
core->Set("EnableCustomRTC", bEnableCustomRTC);
|
||||
core->Set("CustomRTCValue", m_customRTCValue);
|
||||
}
|
||||
|
||||
void SConfig::SaveMovieSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* movie = ini.GetOrCreateSection("Movie");
|
||||
|
||||
movie->Set("PauseMovie", m_PauseMovie);
|
||||
movie->Set("Author", m_strMovieAuthor);
|
||||
movie->Set("DumpFrames", m_DumpFrames);
|
||||
movie->Set("DumpFramesSilent", m_DumpFramesSilent);
|
||||
movie->Set("ShowInputDisplay", m_ShowInputDisplay);
|
||||
movie->Set("ShowRTC", m_ShowRTC);
|
||||
}
|
||||
|
||||
void SConfig::SaveDSPSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* dsp = ini.GetOrCreateSection("DSP");
|
||||
|
||||
dsp->Set("EnableJIT", m_DSPEnableJIT);
|
||||
dsp->Set("DumpAudio", m_DumpAudio);
|
||||
dsp->Set("DumpAudioSilent", m_DumpAudioSilent);
|
||||
dsp->Set("DumpUCode", m_DumpUCode);
|
||||
dsp->Set("Backend", sBackend);
|
||||
dsp->Set("Volume", m_Volume);
|
||||
dsp->Set("CaptureLog", m_DSPCaptureLog);
|
||||
|
||||
#ifdef _WIN32
|
||||
dsp->Set("WASAPIDevice", sWASAPIDevice);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SConfig::SaveInputSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* input = ini.GetOrCreateSection("Input");
|
||||
|
||||
input->Set("BackgroundInput", m_BackgroundInput);
|
||||
}
|
||||
|
||||
void SConfig::SaveFifoPlayerSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* fifoplayer = ini.GetOrCreateSection("FifoPlayer");
|
||||
|
||||
fifoplayer->Set("LoopReplay", bLoopFifoReplay);
|
||||
}
|
||||
|
||||
void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("BluetoothPassthrough");
|
||||
|
||||
section->Set("Enabled", m_bt_passthrough_enabled);
|
||||
section->Set("VID", m_bt_passthrough_vid);
|
||||
section->Set("PID", m_bt_passthrough_pid);
|
||||
section->Set("LinkKeys", m_bt_passthrough_link_keys);
|
||||
}
|
||||
|
||||
void SConfig::SaveUSBPassthroughSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
|
||||
|
||||
std::ostringstream oss;
|
||||
for (const auto& device : m_usb_passthrough_devices)
|
||||
oss << fmt::format("{:04x}:{:04x}", device.first, device.second) << ',';
|
||||
std::string devices_string = oss.str();
|
||||
if (!devices_string.empty())
|
||||
devices_string.pop_back();
|
||||
|
||||
section->Set("Devices", devices_string);
|
||||
}
|
||||
|
||||
void SConfig::SaveAutoUpdateSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("AutoUpdate");
|
||||
|
||||
section->Set("UpdateTrack", m_auto_update_track);
|
||||
section->Set("HashOverride", m_auto_update_hash_override);
|
||||
}
|
||||
|
||||
void SConfig::SaveJitDebugSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("Debug");
|
||||
|
||||
section->Set("JitOff", bJITOff);
|
||||
section->Set("JitLoadStoreOff", bJITLoadStoreOff);
|
||||
section->Set("JitLoadStoreFloatingOff", bJITLoadStoreFloatingOff);
|
||||
section->Set("JitLoadStorePairedOff", bJITLoadStorePairedOff);
|
||||
section->Set("JitFloatingPointOff", bJITFloatingPointOff);
|
||||
section->Set("JitIntegerOff", bJITIntegerOff);
|
||||
section->Set("JitPairedOff", bJITPairedOff);
|
||||
section->Set("JitSystemRegistersOff", bJITSystemRegistersOff);
|
||||
section->Set("JitBranchOff", bJITBranchOff);
|
||||
section->Set("JitRegisterCacheOff", bJITRegisterCacheOff);
|
||||
}
|
||||
|
||||
void SConfig::LoadSettings()
|
||||
{
|
||||
INFO_LOG_FMT(BOOT, "Loading Settings from {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
Config::Load();
|
||||
|
||||
INFO_LOG_FMT(BOOT, "Loading Settings from {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
IniFile ini;
|
||||
ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
|
||||
LoadGeneralSettings(ini);
|
||||
LoadInterfaceSettings(ini);
|
||||
LoadGameListSettings(ini);
|
||||
LoadCoreSettings(ini);
|
||||
LoadMovieSettings(ini);
|
||||
LoadDSPSettings(ini);
|
||||
LoadInputSettings(ini);
|
||||
LoadFifoPlayerSettings(ini);
|
||||
LoadBluetoothPassthroughSettings(ini);
|
||||
LoadUSBPassthroughSettings(ini);
|
||||
LoadAutoUpdateSettings(ini);
|
||||
LoadJitDebugSettings(ini);
|
||||
}
|
||||
|
||||
void SConfig::LoadGeneralSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* general = ini.GetOrCreateSection("General");
|
||||
|
||||
general->Get("ShowLag", &m_ShowLag, false);
|
||||
general->Get("ShowFrameCount", &m_ShowFrameCount, false);
|
||||
#ifdef USE_GDBSTUB
|
||||
#ifndef _WIN32
|
||||
general->Get("GDBSocket", &gdb_socket, "");
|
||||
#endif
|
||||
general->Get("GDBPort", &(iGDBPort), -1);
|
||||
#endif
|
||||
|
||||
m_ISOFolder.clear();
|
||||
int numISOPaths;
|
||||
|
||||
if (general->Get("ISOPaths", &numISOPaths, 0))
|
||||
{
|
||||
for (int i = 0; i < numISOPaths; i++)
|
||||
{
|
||||
std::string tmpPath;
|
||||
general->Get(fmt::format("ISOPath{}", i), &tmpPath, "");
|
||||
m_ISOFolder.push_back(std::move(tmpPath));
|
||||
}
|
||||
}
|
||||
|
||||
general->Get("WirelessMac", &m_WirelessMac);
|
||||
}
|
||||
|
||||
void SConfig::LoadInterfaceSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* interface = ini.GetOrCreateSection("Interface");
|
||||
|
||||
interface->Get("ConfirmStop", &bConfirmStop, true);
|
||||
interface->Get("HideCursor", &bHideCursor, false);
|
||||
interface->Get("LanguageCode", &m_InterfaceLanguage, "");
|
||||
interface->Get("ExtendedFPSInfo", &m_InterfaceExtendedFPSInfo, false);
|
||||
interface->Get("ShowActiveTitle", &m_show_active_title, true);
|
||||
interface->Get("UseBuiltinTitleDatabase", &m_use_builtin_title_database, true);
|
||||
interface->Get("ThemeName", &theme_name, DEFAULT_THEME_DIR);
|
||||
interface->Get("PauseOnFocusLost", &m_PauseOnFocusLost, false);
|
||||
interface->Get("DebugModeEnabled", &bEnableDebugging, false);
|
||||
}
|
||||
|
||||
void SConfig::LoadGameListSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* gamelist = ini.GetOrCreateSection("GameList");
|
||||
|
||||
gamelist->Get("ListDrives", &m_ListDrives, false);
|
||||
gamelist->Get("ListWad", &m_ListWad, true);
|
||||
gamelist->Get("ListElfDol", &m_ListElfDol, true);
|
||||
gamelist->Get("ListWii", &m_ListWii, true);
|
||||
gamelist->Get("ListGC", &m_ListGC, true);
|
||||
gamelist->Get("ListJap", &m_ListJap, true);
|
||||
gamelist->Get("ListPal", &m_ListPal, true);
|
||||
gamelist->Get("ListUsa", &m_ListUsa, true);
|
||||
|
||||
gamelist->Get("ListAustralia", &m_ListAustralia, true);
|
||||
gamelist->Get("ListFrance", &m_ListFrance, true);
|
||||
gamelist->Get("ListGermany", &m_ListGermany, true);
|
||||
gamelist->Get("ListItaly", &m_ListItaly, true);
|
||||
gamelist->Get("ListKorea", &m_ListKorea, true);
|
||||
gamelist->Get("ListNetherlands", &m_ListNetherlands, true);
|
||||
gamelist->Get("ListRussia", &m_ListRussia, true);
|
||||
gamelist->Get("ListSpain", &m_ListSpain, true);
|
||||
gamelist->Get("ListTaiwan", &m_ListTaiwan, true);
|
||||
gamelist->Get("ListWorld", &m_ListWorld, true);
|
||||
gamelist->Get("ListUnknown", &m_ListUnknown, true);
|
||||
gamelist->Get("ListSort", &m_ListSort, 3);
|
||||
gamelist->Get("ListSortSecondary", &m_ListSort2, 0);
|
||||
|
||||
// Gamelist columns toggles
|
||||
gamelist->Get("ColumnPlatform", &m_showSystemColumn, true);
|
||||
gamelist->Get("ColumnDescription", &m_showDescriptionColumn, false);
|
||||
gamelist->Get("ColumnBanner", &m_showBannerColumn, true);
|
||||
gamelist->Get("ColumnTitle", &m_showTitleColumn, true);
|
||||
gamelist->Get("ColumnNotes", &m_showMakerColumn, true);
|
||||
gamelist->Get("ColumnFileName", &m_showFileNameColumn, false);
|
||||
gamelist->Get("ColumnFilePath", &m_showFilePathColumn, false);
|
||||
gamelist->Get("ColumnID", &m_showIDColumn, false);
|
||||
gamelist->Get("ColumnRegion", &m_showRegionColumn, true);
|
||||
gamelist->Get("ColumnSize", &m_showSizeColumn, true);
|
||||
gamelist->Get("ColumnFileFormat", &m_showFileFormatColumn, false);
|
||||
gamelist->Get("ColumnBlockSize", &m_showBlockSizeColumn, false);
|
||||
gamelist->Get("ColumnCompression", &m_showCompressionColumn, false);
|
||||
gamelist->Get("ColumnTags", &m_showTagsColumn, false);
|
||||
}
|
||||
|
||||
void SConfig::LoadCoreSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* core = ini.GetOrCreateSection("Core");
|
||||
|
||||
core->Get("SkipIPL", &bHLE_BS2, true);
|
||||
#ifdef _M_X86
|
||||
core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::JIT64);
|
||||
#elif _M_ARM_64
|
||||
core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::JITARM64);
|
||||
#else
|
||||
core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::Interpreter);
|
||||
#endif
|
||||
core->Get("JITFollowBranch", &bJITFollowBranch, true);
|
||||
core->Get("Fastmem", &bFastmem, true);
|
||||
core->Get("DSPHLE", &bDSPHLE, true);
|
||||
core->Get("TimingVariance", &iTimingVariance, 40);
|
||||
core->Get("CPUThread", &bCPUThread, true);
|
||||
core->Get("SyncOnSkipIdle", &bSyncGPUOnSkipIdleHack, true);
|
||||
core->Get("EnableCheats", &bEnableCheats, false);
|
||||
core->Get("SelectedLanguage", &SelectedLanguage, 0);
|
||||
core->Get("OverrideRegionSettings", &bOverrideRegionSettings, false);
|
||||
core->Get("DPL2Decoder", &bDPL2Decoder, false);
|
||||
core->Get("AudioLatency", &iLatency, 20);
|
||||
core->Get("AudioStretch", &m_audio_stretch, false);
|
||||
core->Get("AudioStretchMaxLatency", &m_audio_stretch_max_latency, 80);
|
||||
core->Get("AgpCartAPath", &m_strGbaCartA);
|
||||
core->Get("AgpCartBPath", &m_strGbaCartB);
|
||||
core->Get("SlotA", (int*)&m_EXIDevice[0], ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER);
|
||||
core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE);
|
||||
core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE);
|
||||
core->Get("BBA_MAC", &m_bba_mac);
|
||||
core->Get("BBA_XLINK_IP", &m_bba_xlink_ip, "127.0.0.1");
|
||||
core->Get("BBA_XLINK_CHAT_OSD", &m_bba_xlink_chat_osd, true);
|
||||
for (size_t i = 0; i < std::size(m_SIDevice); ++i)
|
||||
{
|
||||
//OpenEmu change all controllers to GaceCube Controllers
|
||||
//core->Get(fmt::format("SIDevice{}", i), &m_SIDevice[i],
|
||||
//(i == 0) ? SerialInterface::SIDEVICE_GC_CONTROLLER : SerialInterface::SIDEVICE_NONE);
|
||||
core->Get(fmt::format("SIDevice{}", i), &m_SIDevice[i], SerialInterface::SIDEVICE_GC_CONTROLLER);
|
||||
core->Get(fmt::format("AdapterRumble{}", i), &m_AdapterRumble[i], true);
|
||||
core->Get(fmt::format("SimulateKonga{}", i), &m_AdapterKonga[i], false);
|
||||
}
|
||||
core->Get("WiiSDCard", &m_WiiSDCard, true);
|
||||
core->Get("WiiKeyboard", &m_WiiKeyboard, false);
|
||||
core->Get("WiimoteContinuousScanning", &m_WiimoteContinuousScanning, false);
|
||||
core->Get("WiimoteEnableSpeaker", &m_WiimoteEnableSpeaker, false);
|
||||
core->Get("WiimoteControllerInterface", &connect_wiimotes_for_ciface, false);
|
||||
core->Get("RunCompareServer", &bRunCompareServer, false);
|
||||
core->Get("RunCompareClient", &bRunCompareClient, false);
|
||||
core->Get("MMU", &bMMU, bMMU);
|
||||
core->Get("BBDumpPort", &iBBDumpPort, -1);
|
||||
core->Get("SyncGPU", &bSyncGPU, false);
|
||||
core->Get("SyncGpuMaxDistance", &iSyncGpuMaxDistance, 200000);
|
||||
core->Get("SyncGpuMinDistance", &iSyncGpuMinDistance, -200000);
|
||||
core->Get("SyncGpuOverclock", &fSyncGpuOverclock, 1.0f);
|
||||
core->Get("FastDiscSpeed", &bFastDiscSpeed, false);
|
||||
core->Get("LowDCBZHack", &bLowDCBZHack, false);
|
||||
core->Get("FPRF", &bFPRF, false);
|
||||
core->Get("AccurateNaNs", &bAccurateNaNs, false);
|
||||
core->Get("EmulationSpeed", &m_EmulationSpeed, 1.0f);
|
||||
core->Get("Overclock", &m_OCFactor, 1.0f);
|
||||
core->Get("OverclockEnable", &m_OCEnable, false);
|
||||
core->Get("GPUDeterminismMode", &m_strGPUDeterminismMode, "auto");
|
||||
core->Get("PerfMapDir", &m_perfDir, "");
|
||||
core->Get("EnableCustomRTC", &bEnableCustomRTC, false);
|
||||
// Default to seconds between 1.1.1970 and 1.1.2000
|
||||
core->Get("CustomRTCValue", &m_customRTCValue, 946684800);
|
||||
}
|
||||
|
||||
void SConfig::LoadMovieSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* movie = ini.GetOrCreateSection("Movie");
|
||||
|
||||
movie->Get("PauseMovie", &m_PauseMovie, false);
|
||||
movie->Get("Author", &m_strMovieAuthor, "");
|
||||
movie->Get("DumpFrames", &m_DumpFrames, false);
|
||||
movie->Get("DumpFramesSilent", &m_DumpFramesSilent, false);
|
||||
movie->Get("ShowInputDisplay", &m_ShowInputDisplay, false);
|
||||
movie->Get("ShowRTC", &m_ShowRTC, false);
|
||||
}
|
||||
|
||||
void SConfig::LoadDSPSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* dsp = ini.GetOrCreateSection("DSP");
|
||||
|
||||
dsp->Get("EnableJIT", &m_DSPEnableJIT, true);
|
||||
dsp->Get("DumpAudio", &m_DumpAudio, false);
|
||||
dsp->Get("DumpAudioSilent", &m_DumpAudioSilent, false);
|
||||
dsp->Get("DumpUCode", &m_DumpUCode, false);
|
||||
dsp->Get("Backend", &sBackend, AudioCommon::GetDefaultSoundBackend());
|
||||
dsp->Get("Volume", &m_Volume, 100);
|
||||
dsp->Get("CaptureLog", &m_DSPCaptureLog, false);
|
||||
|
||||
#ifdef _WIN32
|
||||
dsp->Get("WASAPIDevice", &sWASAPIDevice, "default");
|
||||
#endif
|
||||
|
||||
m_IsMuted = false;
|
||||
}
|
||||
|
||||
void SConfig::LoadInputSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* input = ini.GetOrCreateSection("Input");
|
||||
|
||||
input->Get("BackgroundInput", &m_BackgroundInput, false);
|
||||
}
|
||||
|
||||
void SConfig::LoadFifoPlayerSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* fifoplayer = ini.GetOrCreateSection("FifoPlayer");
|
||||
|
||||
fifoplayer->Get("LoopReplay", &bLoopFifoReplay, true);
|
||||
}
|
||||
|
||||
void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("BluetoothPassthrough");
|
||||
|
||||
section->Get("Enabled", &m_bt_passthrough_enabled, false);
|
||||
section->Get("VID", &m_bt_passthrough_vid, -1);
|
||||
section->Get("PID", &m_bt_passthrough_pid, -1);
|
||||
section->Get("LinkKeys", &m_bt_passthrough_link_keys, "");
|
||||
}
|
||||
|
||||
void SConfig::LoadUSBPassthroughSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
|
||||
m_usb_passthrough_devices.clear();
|
||||
std::string devices_string;
|
||||
section->Get("Devices", &devices_string, "");
|
||||
for (const auto& pair : SplitString(devices_string, ','))
|
||||
{
|
||||
const auto index = pair.find(':');
|
||||
if (index == std::string::npos)
|
||||
continue;
|
||||
|
||||
const u16 vid = static_cast<u16>(strtol(pair.substr(0, index).c_str(), nullptr, 16));
|
||||
const u16 pid = static_cast<u16>(strtol(pair.substr(index + 1).c_str(), nullptr, 16));
|
||||
if (vid && pid)
|
||||
m_usb_passthrough_devices.emplace(vid, pid);
|
||||
}
|
||||
}
|
||||
|
||||
void SConfig::LoadAutoUpdateSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("AutoUpdate");
|
||||
|
||||
section->Get("UpdateTrack", &m_auto_update_track, SCM_UPDATE_TRACK_STR);
|
||||
section->Get("HashOverride", &m_auto_update_hash_override, "");
|
||||
}
|
||||
|
||||
void SConfig::LoadJitDebugSettings(IniFile& ini)
|
||||
{
|
||||
IniFile::Section* section = ini.GetOrCreateSection("Debug");
|
||||
section->Get("JitOff", &bJITOff, false);
|
||||
section->Get("JitLoadStoreOff", &bJITLoadStoreOff, false);
|
||||
section->Get("JitLoadStoreFloatingOff", &bJITLoadStoreFloatingOff, false);
|
||||
section->Get("JitLoadStorePairedOff", &bJITLoadStorePairedOff, false);
|
||||
section->Get("JitFloatingPointOff", &bJITFloatingPointOff, false);
|
||||
section->Get("JitIntegerOff", &bJITIntegerOff, false);
|
||||
section->Get("JitPairedOff", &bJITPairedOff, false);
|
||||
section->Get("JitSystemRegistersOff", &bJITSystemRegistersOff, false);
|
||||
section->Get("JitBranchOff", &bJITBranchOff, false);
|
||||
section->Get("JitRegisterCacheOff", &bJITRegisterCacheOff, false);
|
||||
}
|
||||
|
||||
void SConfig::ResetRunningGameMetadata()
|
||||
@@ -169,25 +695,130 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
||||
m_title_description = title_database.Describe(m_gametdb_id, language);
|
||||
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
|
||||
Host_TitleChanged();
|
||||
if (Core::IsRunning())
|
||||
{
|
||||
Core::UpdateTitle();
|
||||
}
|
||||
|
||||
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
|
||||
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
|
||||
|
||||
if (Core::IsRunning())
|
||||
{
|
||||
// TODO: have a callback mechanism for title changes?
|
||||
if (!g_symbolDB.IsEmpty())
|
||||
{
|
||||
g_symbolDB.Clear();
|
||||
Host_NotifyMapLoaded();
|
||||
}
|
||||
CBoot::LoadMapFromFilename();
|
||||
HLE::Reload();
|
||||
PatchEngine::Reload();
|
||||
HiresTexture::Update();
|
||||
DolphinAnalytics::Instance().ReportGameStart();
|
||||
}
|
||||
}
|
||||
|
||||
void SConfig::LoadDefaults()
|
||||
{
|
||||
bEnableDebugging = false;
|
||||
bAutomaticStart = false;
|
||||
bBootToPause = false;
|
||||
|
||||
#ifdef USE_GDBSTUB
|
||||
iGDBPort = -1;
|
||||
#ifndef _WIN32
|
||||
gdb_socket = "";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
cpu_core = PowerPC::DefaultCPUCore();
|
||||
iTimingVariance = 40;
|
||||
bCPUThread = false;
|
||||
bSyncGPUOnSkipIdleHack = true;
|
||||
bRunCompareServer = false;
|
||||
bDSPHLE = true;
|
||||
bFastmem = true;
|
||||
bFPRF = false;
|
||||
bAccurateNaNs = false;
|
||||
bMMU = false;
|
||||
bLowDCBZHack = false;
|
||||
iBBDumpPort = -1;
|
||||
bSyncGPU = false;
|
||||
bFastDiscSpeed = false;
|
||||
bEnableMemcardSdWriting = true;
|
||||
SelectedLanguage = 0;
|
||||
bOverrideRegionSettings = false;
|
||||
bWii = false;
|
||||
bDPL2Decoder = false;
|
||||
iLatency = 20;
|
||||
m_audio_stretch = false;
|
||||
m_audio_stretch_max_latency = 80;
|
||||
|
||||
bLoopFifoReplay = true;
|
||||
|
||||
bJITOff = false; // debugger only settings
|
||||
bJITLoadStoreOff = false;
|
||||
bJITLoadStoreFloatingOff = false;
|
||||
bJITLoadStorePairedOff = false;
|
||||
bJITFloatingPointOff = false;
|
||||
bJITIntegerOff = false;
|
||||
bJITPairedOff = false;
|
||||
bJITSystemRegistersOff = false;
|
||||
bJITBranchOff = false;
|
||||
bJITRegisterCacheOff = false;
|
||||
|
||||
ResetRunningGameMetadata();
|
||||
}
|
||||
|
||||
bool SConfig::IsUSBDeviceWhitelisted(const std::pair<u16, u16> vid_pid) const
|
||||
{
|
||||
return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end();
|
||||
}
|
||||
|
||||
// The reason we need this function is because some memory card code
|
||||
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
|
||||
DiscIO::Region SConfig::ToGameCubeRegion(DiscIO::Region region)
|
||||
{
|
||||
if (region != DiscIO::Region::NTSC_K)
|
||||
return region;
|
||||
|
||||
// GameCube has no NTSC-K region. No choice of replacement value is completely
|
||||
// non-arbitrary, but let's go with NTSC-J since Korean GameCubes are NTSC-J.
|
||||
return DiscIO::Region::NTSC_J;
|
||||
}
|
||||
|
||||
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
|
||||
{
|
||||
if (region == DiscIO::Region::Unknown)
|
||||
region = ToGameCubeRegion(GetFallbackRegion());
|
||||
|
||||
switch (region)
|
||||
{
|
||||
case DiscIO::Region::NTSC_J:
|
||||
return JAP_DIR;
|
||||
|
||||
case DiscIO::Region::NTSC_U:
|
||||
return USA_DIR;
|
||||
|
||||
case DiscIO::Region::PAL:
|
||||
return EUR_DIR;
|
||||
|
||||
case DiscIO::Region::NTSC_K:
|
||||
ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
|
||||
return JAP_DIR; // See ToGameCubeRegion
|
||||
|
||||
default:
|
||||
ASSERT_MSG(BOOT, false, "Default case should not be reached");
|
||||
return EUR_DIR;
|
||||
}
|
||||
}
|
||||
|
||||
std::string SConfig::GetBootROMPath(const std::string& region_directory) const
|
||||
{
|
||||
const std::string path =
|
||||
File::GetUserPath(D_GCUSER_IDX) + DIR_SEP + region_directory + DIR_SEP GC_IPL;
|
||||
if (!File::Exists(path))
|
||||
return File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + region_directory + DIR_SEP GC_IPL;
|
||||
return path;
|
||||
}
|
||||
|
||||
struct SetGameMetadata
|
||||
{
|
||||
SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {}
|
||||
@@ -289,23 +920,29 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
|
||||
return false;
|
||||
|
||||
if (m_region == DiscIO::Region::Unknown)
|
||||
m_region = Config::Get(Config::MAIN_FALLBACK_REGION);
|
||||
m_region = GetFallbackRegion();
|
||||
|
||||
// Set up paths
|
||||
const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region));
|
||||
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
|
||||
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
||||
m_strBootROM = Config::GetBootROMPath(region_dir);
|
||||
m_strBootROM = GetBootROMPath(region_dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DiscIO::Region SConfig::GetFallbackRegion()
|
||||
{
|
||||
return Config::Get(Config::MAIN_FALLBACK_REGION);
|
||||
}
|
||||
|
||||
DiscIO::Language SConfig::GetCurrentLanguage(bool wii) const
|
||||
{
|
||||
DiscIO::Language language;
|
||||
int language_value;
|
||||
if (wii)
|
||||
language = static_cast<DiscIO::Language>(Config::Get(Config::SYSCONF_LANGUAGE));
|
||||
language_value = Config::Get(Config::SYSCONF_LANGUAGE);
|
||||
else
|
||||
language = DiscIO::FromGameCubeLanguage(Config::Get(Config::MAIN_GC_LANGUAGE));
|
||||
language_value = SConfig::GetInstance().SelectedLanguage + 1;
|
||||
DiscIO::Language language = static_cast<DiscIO::Language>(language_value);
|
||||
|
||||
// Get rid of invalid values (probably doesn't matter, but might as well do it)
|
||||
if (language > DiscIO::Language::Unknown || language < DiscIO::Language::Japanese)
|
||||
@@ -323,7 +960,7 @@ DiscIO::Language SConfig::GetLanguageAdjustedForRegion(bool wii, DiscIO::Region
|
||||
if (!wii && region == DiscIO::Region::NTSC_J && language == DiscIO::Language::English)
|
||||
return DiscIO::Language::Japanese; // English and Japanese both use the value 0 in GC SRAM
|
||||
|
||||
if (!Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS))
|
||||
if (!bOverrideRegionSettings)
|
||||
{
|
||||
if (region == DiscIO::Region::NTSC_J)
|
||||
return DiscIO::Language::Japanese;
|
||||
@@ -388,18 +1025,7 @@ IniFile SConfig::LoadGameIni(const std::string& id, std::optional<u16> revision)
|
||||
return game_ini;
|
||||
}
|
||||
|
||||
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard &guard)
|
||||
bool SConfig::ShouldUseDPL2Decoder() const
|
||||
{
|
||||
if (!Core::IsRunning())
|
||||
return;
|
||||
|
||||
if (!g_symbolDB.IsEmpty())
|
||||
{
|
||||
g_symbolDB.Clear();
|
||||
Host_NotifyMapLoaded();
|
||||
}
|
||||
CBoot::LoadMapFromFilename(guard);
|
||||
HLE::Reload(Core::System::GetInstance());
|
||||
PatchEngine::Reload();
|
||||
HiresTexture::Update();
|
||||
return bDPL2Decoder && !bDSPHLE;
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright (c) 2022, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit64_Tables.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_Branch.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_FloatingPoint.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_Integer.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_LoadStore.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_LoadStoreFloating.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_LoadStorePaired.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_Paired.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64/JitAsm.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/BlockCache.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/ConstantPool.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/EmuCodeBlock.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/FarCodeCache.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/Jit64AsmCommon.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/Jit64Common/TrampolineCache.cpp"
|
||||
#elif defined(__arm64__)
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/Jit_Util.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/Jit.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_Branch.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_FloatingPoint.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_Integer.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStorePaired.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_Paired.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp"
|
||||
#include "../../dolphin/Source/Core/Core/PowerPC/JitArm64/JitArm64_BackPatch.cpp"
|
||||
#endif
|
||||
@@ -44,7 +44,6 @@
|
||||
#endif
|
||||
|
||||
ControllerInterface g_controller_interface;
|
||||
static bool m_is_populating_devices = false;
|
||||
|
||||
void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
||||
{
|
||||
@@ -82,7 +81,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
||||
// nothing needed
|
||||
#endif
|
||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||
m_input_backends.emplace_back(ciface::DualShockUDPClient::CreateInputBackend(this));
|
||||
ciface::DualShockUDPClient::Init();
|
||||
#endif
|
||||
|
||||
//OpenEmu initalize OpenEmu Input
|
||||
@@ -94,34 +93,7 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi)
|
||||
m_is_populating_devices = false;
|
||||
}
|
||||
|
||||
static thread_local ciface::InputChannel tls_input_channel = ciface::InputChannel::Host;
|
||||
|
||||
void ControllerInterface::SetCurrentInputChannel(ciface::InputChannel input_channel)
|
||||
{
|
||||
tls_input_channel = input_channel;
|
||||
}
|
||||
|
||||
ciface::InputChannel ControllerInterface::GetCurrentInputChannel()
|
||||
{
|
||||
return tls_input_channel;
|
||||
}
|
||||
|
||||
void ControllerInterface::PlatformPopulateDevices(std::function<void()> callback)
|
||||
{
|
||||
if (!m_is_init)
|
||||
return;
|
||||
|
||||
std::lock_guard lk_population(m_devices_population_mutex);
|
||||
|
||||
m_populating_devices_counter.fetch_add(1);
|
||||
|
||||
callback();
|
||||
|
||||
if (m_populating_devices_counter.fetch_sub(1) == 1)
|
||||
InvokeDevicesChangedCallbacks();
|
||||
}
|
||||
|
||||
void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason)
|
||||
void ControllerInterface::ChangeWindow(void* hwnd)
|
||||
{
|
||||
if (!m_is_init)
|
||||
return;
|
||||
@@ -131,7 +103,7 @@ void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason)
|
||||
RefreshDevices();
|
||||
}
|
||||
|
||||
void ControllerInterface::RefreshDevices(RefreshReason reason)
|
||||
void ControllerInterface::RefreshDevices()
|
||||
{
|
||||
if (!m_is_init)
|
||||
return;
|
||||
@@ -173,6 +145,9 @@ void ControllerInterface::RefreshDevices(RefreshReason reason)
|
||||
#ifdef CIFACE_USE_PIPES
|
||||
ciface::Pipes::PopulateDevices();
|
||||
#endif
|
||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||
ciface::DualShockUDPClient::PopulateDevices();
|
||||
#endif
|
||||
|
||||
WiimoteReal::ProcessWiimotePool();
|
||||
|
||||
@@ -225,13 +200,16 @@ void ControllerInterface::Shutdown()
|
||||
#ifdef CIFACE_USE_EVDEV
|
||||
ciface::evdev::Shutdown();
|
||||
#endif
|
||||
#ifdef CIFACE_USE_DUALSHOCKUDPCLIENT
|
||||
ciface::DualShockUDPClient::DeInit();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device)
|
||||
void ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device)
|
||||
{
|
||||
// If we are shutdown (or in process of shutting down) ignore this request:
|
||||
if (!m_is_init)
|
||||
return false;
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard lk(m_devices_mutex);
|
||||
@@ -259,23 +237,22 @@ bool ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
|
||||
device->SetId(id);
|
||||
}
|
||||
|
||||
NOTICE_LOG_FMT(CONTROLLERINTERFACE, "Added device: {}", device->GetQualifiedName());
|
||||
NOTICE_LOG(SERIALINTERFACE, "Added device: %s", device->GetQualifiedName().c_str());
|
||||
m_devices.emplace_back(std::move(device));
|
||||
}
|
||||
|
||||
if (!m_is_populating_devices)
|
||||
InvokeDevicesChangedCallbacks();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback, bool force_devices_release)
|
||||
void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback)
|
||||
{
|
||||
{
|
||||
std::lock_guard lk(m_devices_mutex);
|
||||
auto it = std::remove_if(m_devices.begin(), m_devices.end(), [&callback](const auto& dev) {
|
||||
if (callback(dev.get()))
|
||||
{
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE, "Removed device: {}", dev->GetQualifiedName());
|
||||
NOTICE_LOG(SERIALINTERFACE, "Removed device: %s", dev->GetQualifiedName().c_str());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -26,7 +26,7 @@ InputConfig::InputConfig(const std::string& ini_name, const std::string& gui_nam
|
||||
|
||||
InputConfig::~InputConfig() = default;
|
||||
|
||||
bool InputConfig::LoadConfig(InputClass type)
|
||||
bool InputConfig::LoadConfig(bool isGC)
|
||||
{
|
||||
//OpenEmu Stub
|
||||
return false;
|
||||
@@ -45,7 +45,7 @@ void InputConfig::SaveConfig()
|
||||
inifile.Save(ini_filename);
|
||||
}
|
||||
|
||||
ControllerEmu::EmulatedController* InputConfig::GetController(int index) const
|
||||
ControllerEmu::EmulatedController* InputConfig::GetController(int index)
|
||||
{
|
||||
return m_controllers.at(index).get();
|
||||
}
|
||||
@@ -60,9 +60,9 @@ bool InputConfig::ControllersNeedToBeCreated() const
|
||||
return m_controllers.empty();
|
||||
}
|
||||
|
||||
int InputConfig::GetControllerCount() const
|
||||
std::size_t InputConfig::GetControllerCount() const
|
||||
{
|
||||
return static_cast<int>(m_controllers.size());
|
||||
return m_controllers.size();
|
||||
}
|
||||
|
||||
void InputConfig::RegisterHotplugCallback()
|
||||
@@ -80,6 +80,11 @@ void InputConfig::UnregisterHotplugCallback()
|
||||
g_controller_interface.UnregisterDevicesChangedCallback(m_hotplug_callback_handle);
|
||||
}
|
||||
|
||||
void InputConfig::OnControllerCreated(ControllerEmu::EmulatedController& controller)
|
||||
{
|
||||
controller.SetDynamicInputTextureManager(&m_dynamic_input_tex_config_manager);
|
||||
}
|
||||
|
||||
bool InputConfig::IsControllerControlledByGamepadDevice(int index) const
|
||||
{
|
||||
if (static_cast<size_t>(index) >= m_controllers.size())
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#include "OpenEmuController.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
|
||||
void input_poll_f()
|
||||
{
|
||||
@@ -16,7 +14,7 @@ void input_poll_f()
|
||||
|
||||
int16_t input_state_f(unsigned port, unsigned device, unsigned index, unsigned button)
|
||||
{
|
||||
if (SConfig::GetInstance().bWii && !Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
||||
if (SConfig::GetInstance().bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
{
|
||||
//This is where we must translate the OpenEmu frontend keys presses stored in the keymap to bitmasks for Dolphin.
|
||||
return WiiRemotes[port].wiimote_keymap[button].value;
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/WiimoteSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/HW/GCKeyboard.h"
|
||||
#include "Core/HW/GCPad.h"
|
||||
@@ -276,7 +273,7 @@ static bool init_wiimotes = false;
|
||||
Pad::Initialize();
|
||||
Keyboard::Initialize();
|
||||
|
||||
if (SConfig::GetInstance().bWii && !Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
||||
if (SConfig::GetInstance().bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
{
|
||||
init_wiimotes = true;
|
||||
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
|
||||
@@ -374,7 +371,7 @@ void Input::openemu_set_controller_port_device(unsigned port, unsigned device)
|
||||
gcPad->UpdateReferences(g_controller_interface);
|
||||
Pad::GetConfig()->SaveConfig();
|
||||
|
||||
if (SConfig::GetInstance().bWii && !Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
||||
if (SConfig::GetInstance().bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
{
|
||||
WiimoteEmu::Wiimote* wm = (WiimoteEmu::Wiimote*)Wiimote::GetConfig()->GetController(port);
|
||||
// load an empty inifile section, clears everything
|
||||
@@ -509,34 +506,34 @@ void Input::openemu_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
case OEWiimote:
|
||||
wmExtension->SetSelectedAttachment(ExtensionNumber::NONE);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::Emulated);
|
||||
break;
|
||||
|
||||
case OEWiimoteSW:
|
||||
wmExtension->SetSelectedAttachment(ExtensionNumber::NONE);
|
||||
static_cast<ControllerEmu::NumericSetting<bool>*>(wmOptions->numeric_settings[2].get())
|
||||
->SetValue(true); // Sideways Wiimote
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::Emulated);
|
||||
break;
|
||||
|
||||
case OEWiimoteNC:
|
||||
wmExtension->SetSelectedAttachment(ExtensionNumber::NUNCHUK);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::Emulated);
|
||||
break;
|
||||
|
||||
case OEWiimoteCC:
|
||||
case OEWiimoteCC_Pro:
|
||||
wmExtension->SetSelectedAttachment(ExtensionNumber::CLASSIC);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::Emulated);
|
||||
break;
|
||||
|
||||
case OEWiiMoteReal:
|
||||
//desc = Libretro::Input::descEmpty;
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::Real);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::Real);
|
||||
|
||||
default:
|
||||
//desc = Libretro::Input::descGC;
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(port), WiimoteSource::None);
|
||||
WiimoteCommon::SetSource(port, WiimoteSource::None);
|
||||
break;
|
||||
}
|
||||
wm->UpdateReferences(g_controller_interface);
|
||||
|
||||
@@ -1,524 +0,0 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoBackends/Metal/MTLGfx.h"
|
||||
|
||||
#include "VideoBackends/Metal/MTLBoundingBox.h"
|
||||
#include "VideoBackends/Metal/MTLObjectCache.h"
|
||||
#include "VideoBackends/Metal/MTLPipeline.h"
|
||||
#include "VideoBackends/Metal/MTLStateTracker.h"
|
||||
#include "VideoBackends/Metal/MTLTexture.h"
|
||||
#include "VideoBackends/Metal/MTLUtil.h"
|
||||
#include "VideoBackends/Metal/MTLVertexFormat.h"
|
||||
#include "VideoBackends/Metal/MTLVertexManager.h"
|
||||
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/Present.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
||||
#import "DolphinGameCore.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
Metal::Gfx::Gfx(MRCOwned<CAMetalLayer*> layer) : m_layer(std::move(layer))
|
||||
{
|
||||
UpdateActiveConfig();
|
||||
[m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive];
|
||||
|
||||
SetupSurface();
|
||||
g_state_tracker->FlushEncoders();
|
||||
}
|
||||
|
||||
Metal::Gfx::~Gfx() = default;
|
||||
|
||||
bool Metal::Gfx::IsHeadless() const
|
||||
{
|
||||
return m_layer == nullptr;
|
||||
}
|
||||
|
||||
// MARK: Texture Creation
|
||||
|
||||
std::unique_ptr<AbstractTexture> Metal::Gfx::CreateTexture(const TextureConfig& config,
|
||||
std::string_view name)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
MRCOwned<MTLTextureDescriptor*> desc = MRCTransfer([MTLTextureDescriptor new]);
|
||||
[desc setTextureType:config.samples > 1 ? MTLTextureType2DMultisampleArray :
|
||||
MTLTextureType2DArray];
|
||||
[desc setPixelFormat:Util::FromAbstract(config.format)];
|
||||
[desc setWidth:config.width];
|
||||
[desc setHeight:config.height];
|
||||
[desc setMipmapLevelCount:config.levels];
|
||||
[desc setArrayLength:config.layers];
|
||||
[desc setSampleCount:config.samples];
|
||||
[desc setStorageMode:MTLStorageModePrivate];
|
||||
MTLTextureUsage usage = MTLTextureUsageShaderRead;
|
||||
if (config.IsRenderTarget())
|
||||
usage |= MTLTextureUsageRenderTarget;
|
||||
if (config.IsComputeImage())
|
||||
usage |= MTLTextureUsageShaderWrite;
|
||||
[desc setUsage:usage];
|
||||
id<MTLTexture> texture = [g_device newTextureWithDescriptor:desc];
|
||||
if (!texture)
|
||||
return nullptr;
|
||||
|
||||
if (name.empty())
|
||||
[texture setLabel:[NSString stringWithFormat:@"Texture %d", m_texture_counter++]];
|
||||
else
|
||||
[texture setLabel:MRCTransfer([[NSString alloc] initWithBytes:name.data()
|
||||
length:name.size()
|
||||
encoding:NSUTF8StringEncoding])];
|
||||
return std::make_unique<Texture>(MRCTransfer(texture), config);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractStagingTexture>
|
||||
Metal::Gfx::CreateStagingTexture(StagingTextureType type, const TextureConfig& config)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
const size_t stride = config.GetStride();
|
||||
const size_t buffer_size = stride * static_cast<size_t>(config.height);
|
||||
|
||||
MTLResourceOptions options = MTLStorageModeShared;
|
||||
if (type == StagingTextureType::Upload)
|
||||
options |= MTLResourceCPUCacheModeWriteCombined;
|
||||
|
||||
id<MTLBuffer> buffer = [g_device newBufferWithLength:buffer_size options:options];
|
||||
if (!buffer)
|
||||
return nullptr;
|
||||
[buffer
|
||||
setLabel:[NSString stringWithFormat:@"Staging Texture %d", m_staging_texture_counter++]];
|
||||
return std::make_unique<StagingTexture>(MRCTransfer(buffer), type, config);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractFramebuffer>
|
||||
Metal::Gfx::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture* depth_attachment)
|
||||
{
|
||||
AbstractTexture* const either_attachment = color_attachment ? color_attachment : depth_attachment;
|
||||
return std::make_unique<Framebuffer>(
|
||||
color_attachment, depth_attachment, either_attachment->GetWidth(),
|
||||
either_attachment->GetHeight(), either_attachment->GetLayers(),
|
||||
either_attachment->GetSamples());
|
||||
}
|
||||
|
||||
// MARK: Pipeline Creation
|
||||
|
||||
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromSource(ShaderStage stage,
|
||||
std::string_view source,
|
||||
std::string_view name)
|
||||
{
|
||||
std::optional<std::string> msl = Util::TranslateShaderToMSL(stage, source);
|
||||
if (!msl.has_value())
|
||||
{
|
||||
PanicAlertFmt("Failed to convert shader {} to MSL", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CreateShaderFromMSL(stage, std::move(*msl), source, name);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromBinary(ShaderStage stage,
|
||||
const void* data, size_t length,
|
||||
std::string_view name)
|
||||
{
|
||||
return CreateShaderFromMSL(stage, std::string(static_cast<const char*>(data), length), {}, name);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
static const char* StageFilename(ShaderStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage::Vertex: return "vs";
|
||||
case ShaderStage::Geometry: return "gs";
|
||||
case ShaderStage::Pixel: return "ps";
|
||||
case ShaderStage::Compute: return "cs";
|
||||
}
|
||||
}
|
||||
|
||||
static NSString* GenericShaderName(ShaderStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage::Vertex: return @"Vertex shader %d";
|
||||
case ShaderStage::Geometry: return @"Geometry shader %d";
|
||||
case ShaderStage::Pixel: return @"Pixel shader %d";
|
||||
case ShaderStage::Compute: return @"Compute shader %d";
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
std::unique_ptr<AbstractShader> Metal::Gfx::CreateShaderFromMSL(ShaderStage stage, std::string msl,
|
||||
std::string_view glsl,
|
||||
std::string_view name)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSError* err = nullptr;
|
||||
auto DumpBadShader = [&](std::string_view msg) {
|
||||
static int counter = 0;
|
||||
std::string filename = VideoBackendBase::BadShaderFilename(StageFilename(stage), counter++);
|
||||
std::ofstream stream(filename);
|
||||
if (stream.good())
|
||||
{
|
||||
stream << msl << std::endl;
|
||||
stream << "/*" << std::endl;
|
||||
stream << msg << std::endl;
|
||||
stream << "Error:" << std::endl;
|
||||
stream << [[err localizedDescription] UTF8String] << std::endl;
|
||||
if (!glsl.empty())
|
||||
{
|
||||
stream << "Original GLSL:" << std::endl;
|
||||
stream << glsl << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream << "Shader was created with cached MSL so no GLSL is available." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
stream << std::endl;
|
||||
stream << "Dolphin Version: " << Common::GetScmRevStr() << std::endl;
|
||||
stream << "Video Backend: " << g_video_backend->GetDisplayName() << std::endl;
|
||||
stream << "*/" << std::endl;
|
||||
stream.close();
|
||||
|
||||
PanicAlertFmt("{} (written to {})\n", msg, filename);
|
||||
};
|
||||
|
||||
auto lib = MRCTransfer([g_device newLibraryWithSource:[NSString stringWithUTF8String:msl.data()]
|
||||
options:nil
|
||||
error:&err]);
|
||||
if (err)
|
||||
{
|
||||
DumpBadShader(fmt::format("Failed to compile {}", name));
|
||||
return nullptr;
|
||||
}
|
||||
auto fn = MRCTransfer([lib newFunctionWithName:@"main0"]);
|
||||
if (!fn)
|
||||
{
|
||||
DumpBadShader(fmt::format("Shader {} is missing its main0 function", name));
|
||||
return nullptr;
|
||||
}
|
||||
if (!name.empty())
|
||||
[fn setLabel:MRCTransfer([[NSString alloc] initWithBytes:name.data()
|
||||
length:name.size()
|
||||
encoding:NSUTF8StringEncoding])];
|
||||
else
|
||||
[fn setLabel:[NSString stringWithFormat:GenericShaderName(stage),
|
||||
m_shader_counter[static_cast<u32>(stage)]++]];
|
||||
[lib setLabel:[fn label]];
|
||||
if (stage == ShaderStage::Compute)
|
||||
{
|
||||
MTLComputePipelineReflection* reflection = nullptr;
|
||||
auto desc = [MTLComputePipelineDescriptor new];
|
||||
[desc setComputeFunction:fn];
|
||||
[desc setLabel:[fn label]];
|
||||
MRCOwned<id<MTLComputePipelineState>> pipeline =
|
||||
MRCTransfer([g_device newComputePipelineStateWithDescriptor:desc
|
||||
options:MTLPipelineOptionArgumentInfo
|
||||
reflection:&reflection
|
||||
error:&err]);
|
||||
if (err)
|
||||
{
|
||||
DumpBadShader(fmt::format("Failed to compile compute pipeline {}", name));
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<ComputePipeline>(stage, reflection, std::move(msl), std::move(fn),
|
||||
std::move(pipeline));
|
||||
}
|
||||
return std::make_unique<Shader>(stage, std::move(msl), std::move(fn));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeVertexFormat>
|
||||
Metal::Gfx::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
return std::make_unique<VertexFormat>(vtx_decl);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractPipeline> Metal::Gfx::CreatePipeline(const AbstractPipelineConfig& config,
|
||||
const void* cache_data,
|
||||
size_t cache_data_length)
|
||||
{
|
||||
return g_object_cache->CreatePipeline(config);
|
||||
}
|
||||
|
||||
void Metal::Gfx::Flush()
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->FlushEncoders();
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::WaitForGPUIdle()
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->FlushEncoders();
|
||||
g_state_tracker->WaitForFlushedEncoders();
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::OnConfigChanged(u32 bits)
|
||||
{
|
||||
AbstractGfx::OnConfigChanged(bits);
|
||||
|
||||
if (bits & CONFIG_CHANGE_BIT_VSYNC)
|
||||
[m_layer setDisplaySyncEnabled:g_ActiveConfig.bVSyncActive];
|
||||
|
||||
if (bits & CONFIG_CHANGE_BIT_ANISOTROPY)
|
||||
{
|
||||
g_object_cache->ReloadSamplers();
|
||||
g_state_tracker->ReloadSamplers();
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool color_enable,
|
||||
bool alpha_enable, bool z_enable, u32 color, u32 z)
|
||||
{
|
||||
u32 framebuffer_width = m_current_framebuffer->GetWidth();
|
||||
u32 framebuffer_height = m_current_framebuffer->GetHeight();
|
||||
// All Metal render passes are fullscreen, so we can only run a fast clear if the target is too
|
||||
if (target_rc == MathUtil::Rectangle<int>(0, 0, framebuffer_width, framebuffer_height))
|
||||
{
|
||||
// Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
|
||||
// channel to 0xFF. This hopefully allows us to use the fast path in most cases.
|
||||
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 ||
|
||||
bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 ||
|
||||
bpmem.zcontrol.pixel_format == PixelFormat::Z24)
|
||||
{
|
||||
// Force alpha writes, and clear the alpha channel. This is different from the other backends,
|
||||
// where the existing values of the alpha channel are preserved.
|
||||
alpha_enable = true;
|
||||
color &= 0x00FFFFFF;
|
||||
}
|
||||
|
||||
bool c_ok = (color_enable && alpha_enable) ||
|
||||
g_state_tracker->GetCurrentFramebuffer()->GetColorFormat() ==
|
||||
AbstractTextureFormat::Undefined;
|
||||
bool z_ok = z_enable || g_state_tracker->GetCurrentFramebuffer()->GetDepthFormat() ==
|
||||
AbstractTextureFormat::Undefined;
|
||||
if (c_ok && z_ok)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
// clang-format off
|
||||
MTLClearColor clear_color = MTLClearColorMake(
|
||||
static_cast<double>((color >> 16) & 0xFF) / 255.0,
|
||||
static_cast<double>((color >> 8) & 0xFF) / 255.0,
|
||||
static_cast<double>((color >> 0) & 0xFF) / 255.0,
|
||||
static_cast<double>((color >> 24) & 0xFF) / 255.0);
|
||||
// clang-format on
|
||||
float z_normalized = static_cast<float>(z & 0xFFFFFF) / 16777216.0f;
|
||||
if (!g_Config.backend_info.bSupportsReversedDepthRange)
|
||||
z_normalized = 1.f - z_normalized;
|
||||
g_state_tracker->BeginClearRenderPass(clear_color, z_normalized);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_state_tracker->EnableEncoderLabel(false);
|
||||
AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z);
|
||||
g_state_tracker->EnableEncoderLabel(true);
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetPipeline(const AbstractPipeline* pipeline)
|
||||
{
|
||||
g_state_tracker->SetPipeline(static_cast<const Pipeline*>(pipeline));
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer)
|
||||
{
|
||||
// Shouldn't be bound as a texture.
|
||||
if (AbstractTexture* color = framebuffer->GetColorAttachment())
|
||||
g_state_tracker->UnbindTexture(static_cast<Texture*>(color)->GetMTLTexture());
|
||||
if (AbstractTexture* depth = framebuffer->GetDepthAttachment())
|
||||
g_state_tracker->UnbindTexture(static_cast<Texture*>(depth)->GetMTLTexture());
|
||||
|
||||
m_current_framebuffer = framebuffer;
|
||||
g_state_tracker->SetCurrentFramebuffer(static_cast<Framebuffer*>(framebuffer));
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
SetFramebuffer(framebuffer);
|
||||
g_state_tracker->BeginRenderPass(MTLLoadActionDontCare);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
|
||||
const ClearColor& color_value, float depth_value)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
SetFramebuffer(framebuffer);
|
||||
MTLClearColor color =
|
||||
MTLClearColorMake(color_value[0], color_value[1], color_value[2], color_value[3]);
|
||||
g_state_tracker->BeginClearRenderPass(color, depth_value);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetScissorRect(const MathUtil::Rectangle<int>& rc)
|
||||
{
|
||||
g_state_tracker->SetScissor(rc);
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetTexture(u32 index, const AbstractTexture* texture)
|
||||
{
|
||||
g_state_tracker->SetTexture(
|
||||
index, texture ? static_cast<const Texture*>(texture)->GetMTLTexture() : nullptr);
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetSamplerState(u32 index, const SamplerState& state)
|
||||
{
|
||||
g_state_tracker->SetSampler(index, state);
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
|
||||
{
|
||||
g_state_tracker->SetComputeTexture(static_cast<const Texture*>(texture));
|
||||
}
|
||||
|
||||
void Metal::Gfx::UnbindTexture(const AbstractTexture* texture)
|
||||
{
|
||||
g_state_tracker->UnbindTexture(static_cast<const Texture*>(texture)->GetMTLTexture());
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetViewport(float x, float y, float width, float height, float near_depth,
|
||||
float far_depth)
|
||||
{
|
||||
g_state_tracker->SetViewport(x, y, width, height, near_depth, far_depth);
|
||||
}
|
||||
|
||||
void Metal::Gfx::Draw(u32 base_vertex, u32 num_vertices)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->Draw(base_vertex, num_vertices);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->DrawIndexed(base_index, num_indices, base_vertex);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::DispatchComputeShader(const AbstractShader* shader, //
|
||||
u32 groupsize_x, u32 groupsize_y, u32 groupsize_z,
|
||||
u32 groups_x, u32 groups_y, u32 groups_z)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->SetPipeline(static_cast<const ComputePipeline*>(shader));
|
||||
g_state_tracker->DispatchComputeShader(groupsize_x, groupsize_y, groupsize_z, //
|
||||
groups_x, groups_y, groups_z);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::BindBackbuffer(const ClearColor& clear_color)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
m_drawable = MRCRetain([m_layer nextDrawable]);
|
||||
m_bb_texture->SetMTLTexture(MRCRetain([m_drawable texture]));
|
||||
SetAndClearFramebuffer(m_backbuffer.get(), clear_color);
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::PresentBackbuffer()
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
g_state_tracker->EndRenderPass();
|
||||
if (m_drawable)
|
||||
{
|
||||
//OpenEmu Blit to GameCoore Texture for Rendering
|
||||
id<MTLBlitCommandEncoder> blitCommandEncoder = [g_state_tracker->GetRenderCmdBuf() blitCommandEncoder];
|
||||
|
||||
if (@available(macOS 10.15, *)) {
|
||||
[blitCommandEncoder copyFromTexture:[m_drawable texture] toTexture:id<MTLTexture>([_current metalTexture])];
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
// TODO: Add pre 10.15 metal blit
|
||||
}
|
||||
|
||||
[blitCommandEncoder endEncoding];
|
||||
|
||||
// PresentDrawable refuses to allow Dolphin to present faster than the display's refresh rate
|
||||
// when windowed (or fullscreen with vsync enabled, but that's more understandable).
|
||||
// On the other hand, it helps Xcode's GPU captures start and stop on frame boundaries
|
||||
// which is convenient. Put it here as a default-off config, which we can override in Xcode.
|
||||
// It also seems to improve frame pacing, so enable it by default with vsync
|
||||
if (g_ActiveConfig.iUsePresentDrawable == TriState::On ||
|
||||
(g_ActiveConfig.iUsePresentDrawable == TriState::Auto && g_ActiveConfig.bVSyncActive))
|
||||
[g_state_tracker->GetRenderCmdBuf() presentDrawable:m_drawable];
|
||||
else
|
||||
[g_state_tracker->GetRenderCmdBuf()
|
||||
addScheduledHandler:[drawable = std::move(m_drawable)](id) { [drawable present]; }];
|
||||
m_bb_texture->SetMTLTexture(nullptr);
|
||||
m_drawable = nullptr;
|
||||
}
|
||||
g_state_tracker->FlushEncoders();
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::Gfx::CheckForSurfaceChange()
|
||||
{
|
||||
if (!g_presenter->SurfaceChangedTestAndClear())
|
||||
return;
|
||||
m_layer = MRCRetain(static_cast<CAMetalLayer*>(g_presenter->GetNewSurfaceHandle()));
|
||||
SetupSurface();
|
||||
}
|
||||
|
||||
void Metal::Gfx::CheckForSurfaceResize()
|
||||
{
|
||||
if (!g_presenter->SurfaceResizedTestAndClear())
|
||||
return;
|
||||
SetupSurface();
|
||||
}
|
||||
|
||||
void Metal::Gfx::SetupSurface()
|
||||
{
|
||||
auto info = GetSurfaceInfo();
|
||||
|
||||
[m_layer setDrawableSize:{static_cast<double>(info.width), static_cast<double>(info.height)}];
|
||||
|
||||
TextureConfig cfg(info.width, info.height, 1, 1, 1, info.format,
|
||||
AbstractTextureFlag_RenderTarget);
|
||||
m_bb_texture = std::make_unique<Texture>(nullptr, cfg);
|
||||
m_backbuffer = std::make_unique<Framebuffer>(m_bb_texture.get(), nullptr, //
|
||||
info.width, info.height, 1, 1);
|
||||
|
||||
if (g_presenter)
|
||||
g_presenter->SetBackbuffer(info);
|
||||
}
|
||||
|
||||
SurfaceInfo Metal::Gfx::GetSurfaceInfo() const
|
||||
{
|
||||
if (!m_layer) // Headless
|
||||
return {};
|
||||
|
||||
CGSize size = [m_layer bounds].size;
|
||||
const float scale = [m_layer contentsScale];
|
||||
|
||||
return {static_cast<u32>(size.width * scale), static_cast<u32>(size.height * scale), scale,
|
||||
Util::ToAbstract([m_layer pixelFormat])};
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoBackends/Metal/VideoBackend.h"
|
||||
|
||||
// This must be included before we use any TARGET_OS_* macros.
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
#include <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "VideoBackends/Metal/MTLBoundingBox.h"
|
||||
#include "VideoBackends/Metal/MTLGfx.h"
|
||||
#include "VideoBackends/Metal/MTLObjectCache.h"
|
||||
#include "VideoBackends/Metal/MTLPerfQuery.h"
|
||||
#include "VideoBackends/Metal/MTLStateTracker.h"
|
||||
#include "VideoBackends/Metal/MTLUtil.h"
|
||||
#include "VideoBackends/Metal/MTLVertexManager.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
#import "DolphinGameCore.h"
|
||||
|
||||
std::string Metal::VideoBackend::GetName() const
|
||||
{
|
||||
return NAME;
|
||||
}
|
||||
|
||||
std::string Metal::VideoBackend::GetDisplayName() const
|
||||
{
|
||||
// i18n: Apple's Metal graphics API (https://developer.apple.com/metal/)
|
||||
return _trans("Metal");
|
||||
}
|
||||
|
||||
std::optional<std::string> Metal::VideoBackend::GetWarningMessage() const
|
||||
{
|
||||
// if (Util::GetAdapterList().empty())
|
||||
// {
|
||||
// return _trans("No Metal-compatible GPUs were found. "
|
||||
// "Use the OpenGL backend or upgrade your computer/GPU");
|
||||
// }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool WindowSystemTypeSupportsMetal(WindowSystemType type)
|
||||
{
|
||||
// switch (type)
|
||||
// {
|
||||
// case WindowSystemType::MacOS:
|
||||
// case WindowSystemType::Headless:
|
||||
return true;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
const bool surface_ok = wsi.type == WindowSystemType::Headless || wsi.render_surface;
|
||||
if (!WindowSystemTypeSupportsMetal(wsi.type) || !surface_ok)
|
||||
{
|
||||
PanicAlertFmt("Bad WindowSystemInfo for Metal renderer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Util::PopulateBackendInfo(&g_Config);
|
||||
std::vector<MRCOwned<id<MTLDevice>>> adapters;
|
||||
adapters.push_back(MRCTransfer([_current metalDevice]));
|
||||
Util::PopulateBackendInfoAdapters(&g_Config, adapters);
|
||||
if (!adapters.empty())
|
||||
{
|
||||
// Use the selected adapter, or the first to fill features.
|
||||
size_t index = static_cast<size_t>(g_Config.iAdapter);
|
||||
if (index >= adapters.size())
|
||||
index = 0;
|
||||
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
|
||||
}
|
||||
|
||||
// Since we haven't called InitializeShared yet, iAdapter may be out of range,
|
||||
// so we have to check it ourselves.
|
||||
size_t selected_adapter_index = static_cast<size_t>(g_Config.iAdapter);
|
||||
if (selected_adapter_index >= adapters.size())
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "Metal adapter index out of range, selecting default adapter.");
|
||||
selected_adapter_index = 0;
|
||||
}
|
||||
MRCOwned<id<MTLDevice>> adapter = MRCOwned<id<MTLDevice>>(MRCRetain([_current metalDevice]));
|
||||
Util::PopulateBackendInfoFeatures(&g_Config, adapter);
|
||||
|
||||
UpdateActiveConfig();
|
||||
|
||||
MRCOwned<CAMetalLayer*> layer = MRCRetain(static_cast<CAMetalLayer*>(wsi.render_surface));
|
||||
//MRCOwned<CAMetalLayer*> layer = MRCRetain([CAMetalLayer layer]);
|
||||
layer.Get().framebufferOnly=NO;
|
||||
[layer setDevice:adapter];
|
||||
|
||||
if (Util::ToAbstract([layer pixelFormat]) == AbstractTextureFormat::Undefined)
|
||||
[layer setPixelFormat:MTLPixelFormatBGRA8Unorm];
|
||||
|
||||
ObjectCache::Initialize(std::move(adapter));
|
||||
g_state_tracker = std::make_unique<StateTracker>();
|
||||
|
||||
return InitializeShared(
|
||||
std::make_unique<Metal::Gfx>(std::move(layer)), std::make_unique<Metal::VertexManager>(),
|
||||
std::make_unique<Metal::PerfQuery>(), std::make_unique<Metal::BoundingBox>());
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::VideoBackend::Shutdown()
|
||||
{
|
||||
ShutdownShared();
|
||||
|
||||
g_state_tracker.reset();
|
||||
ObjectCache::Shutdown();
|
||||
}
|
||||
|
||||
void Metal::VideoBackend::InitBackendInfo()
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
Util::PopulateBackendInfo(&g_Config);
|
||||
std::vector<MRCOwned<id<MTLDevice>>> adapters;
|
||||
adapters.push_back(MRCTransfer([_current metalDevice]));
|
||||
Util::PopulateBackendInfoAdapters(&g_Config, adapters);
|
||||
if (!adapters.empty())
|
||||
{
|
||||
// Use the selected adapter, or the first to fill features.
|
||||
size_t index = static_cast<size_t>(g_Config.iAdapter);
|
||||
if (index >= adapters.size())
|
||||
index = 0;
|
||||
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
|
||||
{
|
||||
//#if TARGET_OS_OSX
|
||||
// if (wsi.type != WindowSystemType::MacOS)
|
||||
// return;
|
||||
// NSView* view = static_cast<NSView*>(wsi.render_surface);
|
||||
CAMetalLayer* layer = [CAMetalLayer layer];
|
||||
// [view setWantsLayer:YES];
|
||||
// [view setLayer:layer];
|
||||
wsi.render_surface = layer;
|
||||
//#endif
|
||||
}
|
||||
@@ -1,731 +0,0 @@
|
||||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoBackends/OGL/OGLGfx.h"
|
||||
|
||||
#include "Common/GL/GLContext.h"
|
||||
#include "Common/GL/GLExtensions/GLExtensions.h"
|
||||
#include "Common/Logging/LogManager.h"
|
||||
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
|
||||
#include "VideoBackends/OGL/OGLConfig.h"
|
||||
#include "VideoBackends/OGL/OGLPipeline.h"
|
||||
#include "VideoBackends/OGL/OGLShader.h"
|
||||
#include "VideoBackends/OGL/OGLTexture.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
#include "VideoBackends/OGL/SamplerCache.h"
|
||||
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/DriverDetails.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/Present.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace OGL
|
||||
{
|
||||
VideoConfig g_ogl_config;
|
||||
|
||||
static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const char* message, const void* userParam)
|
||||
{
|
||||
const char* s_source;
|
||||
const char* s_type;
|
||||
|
||||
// Performance - DualCore driver performance warning:
|
||||
// DualCore application thread syncing with server thread
|
||||
if (id == 0x200b0)
|
||||
return;
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API_ARB:
|
||||
s_source = "API";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
|
||||
s_source = "Window System";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
|
||||
s_source = "Shader Compiler";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
|
||||
s_source = "Third Party";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION_ARB:
|
||||
s_source = "Application";
|
||||
break;
|
||||
case GL_DEBUG_SOURCE_OTHER_ARB:
|
||||
s_source = "Other";
|
||||
break;
|
||||
default:
|
||||
s_source = "Unknown";
|
||||
break;
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR_ARB:
|
||||
s_type = "Error";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
|
||||
s_type = "Deprecated";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
|
||||
s_type = "Undefined";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY_ARB:
|
||||
s_type = "Portability";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE_ARB:
|
||||
s_type = "Performance";
|
||||
break;
|
||||
case GL_DEBUG_TYPE_OTHER_ARB:
|
||||
s_type = "Other";
|
||||
break;
|
||||
default:
|
||||
s_type = "Unknown";
|
||||
break;
|
||||
}
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH_ARB:
|
||||
ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM_ARB:
|
||||
WARN_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_LOW_ARB:
|
||||
DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
DEBUG_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG_FMT(HOST_GPU, "id: {:x}, source: {}, type: {} - {}", id, s_source, s_type, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Two small Fallbacks to avoid GL_ARB_ES2_compatibility
|
||||
static void APIENTRY DepthRangef(GLfloat neardepth, GLfloat fardepth)
|
||||
{
|
||||
glDepthRange(neardepth, fardepth);
|
||||
}
|
||||
static void APIENTRY ClearDepthf(GLfloat depthval)
|
||||
{
|
||||
glClearDepth(depthval);
|
||||
}
|
||||
|
||||
OGLGfx::OGLGfx(std::unique_ptr<GLContext> main_gl_context, float backbuffer_scale)
|
||||
: m_main_gl_context(std::move(main_gl_context)),
|
||||
m_current_rasterization_state(RenderState::GetInvalidRasterizationState()),
|
||||
m_current_depth_state(RenderState::GetInvalidDepthState()),
|
||||
m_current_blend_state(RenderState::GetInvalidBlendingState()),
|
||||
m_backbuffer_scale(backbuffer_scale)
|
||||
{
|
||||
// Create the window framebuffer.
|
||||
if (!m_main_gl_context->IsHeadless())
|
||||
{
|
||||
m_system_framebuffer = std::make_unique<OGLFramebuffer>(
|
||||
nullptr, nullptr, AbstractTextureFormat::RGBA8, AbstractTextureFormat::Undefined,
|
||||
std::max(m_main_gl_context->GetBackBufferWidth(), 1u),
|
||||
std::max(m_main_gl_context->GetBackBufferHeight(), 1u), 1, 1, g_Config.iRenderFBO);
|
||||
m_current_framebuffer = m_system_framebuffer.get();
|
||||
}
|
||||
|
||||
if (!m_main_gl_context->IsGLES())
|
||||
{
|
||||
// OpenGL 3 doesn't provide GLES like float functions for depth.
|
||||
// They are in core in OpenGL 4.1, so almost every driver should support them.
|
||||
// But for the oldest ones, we provide fallbacks to the old double functions.
|
||||
if (!GLExtensions::Supports("GL_ARB_ES2_compatibility"))
|
||||
{
|
||||
glDepthRangef = DepthRangef;
|
||||
glClearDepthf = ClearDepthf;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PopulateConfig(m_main_gl_context.get()))
|
||||
{
|
||||
// Not all needed extensions are supported, so we have to stop here.
|
||||
// Else some of the next calls might crash.
|
||||
return;
|
||||
}
|
||||
InitDriverInfo();
|
||||
|
||||
// Setup Debug logging
|
||||
if (g_ogl_config.bSupportsDebug)
|
||||
{
|
||||
if (GLExtensions::Supports("GL_KHR_debug"))
|
||||
{
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
|
||||
glDebugMessageCallback(ErrorCallback, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, true);
|
||||
glDebugMessageCallbackARB(ErrorCallback, nullptr);
|
||||
}
|
||||
if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU,
|
||||
Common::Log::LogLevel::LERROR))
|
||||
{
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_DEBUG_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle VSync on/off
|
||||
if (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
|
||||
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsClipControl)
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
|
||||
{
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
glEnable(GL_CLIP_DISTANCE1);
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment
|
||||
|
||||
glGenFramebuffers(1, &m_shared_read_framebuffer);
|
||||
glGenFramebuffers(1, &m_shared_draw_framebuffer);
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart)
|
||||
GLUtil::EnablePrimitiveRestart(m_main_gl_context.get());
|
||||
|
||||
UpdateActiveConfig();
|
||||
}
|
||||
|
||||
OGLGfx::~OGLGfx()
|
||||
{
|
||||
glDeleteFramebuffers(1, &m_shared_draw_framebuffer);
|
||||
glDeleteFramebuffers(1, &m_shared_read_framebuffer);
|
||||
}
|
||||
|
||||
bool OGLGfx::IsHeadless() const
|
||||
{
|
||||
return m_main_gl_context->IsHeadless();
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractTexture> OGLGfx::CreateTexture(const TextureConfig& config,
|
||||
std::string_view name)
|
||||
{
|
||||
return std::make_unique<OGLTexture>(config, name);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractStagingTexture> OGLGfx::CreateStagingTexture(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
return OGLStagingTexture::Create(type, config);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractFramebuffer> OGLGfx::CreateFramebuffer(AbstractTexture* color_attachment,
|
||||
AbstractTexture* depth_attachment)
|
||||
{
|
||||
return OGLFramebuffer::Create(static_cast<OGLTexture*>(color_attachment),
|
||||
static_cast<OGLTexture*>(depth_attachment));
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader>
|
||||
OGLGfx::CreateShaderFromSource(ShaderStage stage, std::string_view source, std::string_view name)
|
||||
{
|
||||
return OGLShader::CreateFromSource(stage, source, name);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader>
|
||||
OGLGfx::CreateShaderFromBinary(ShaderStage stage, const void* data, size_t length,
|
||||
[[maybe_unused]] std::string_view name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractPipeline> OGLGfx::CreatePipeline(const AbstractPipelineConfig& config,
|
||||
const void* cache_data,
|
||||
size_t cache_data_length)
|
||||
{
|
||||
return OGLPipeline::Create(config, cache_data, cache_data_length);
|
||||
}
|
||||
|
||||
void OGLGfx::SetScissorRect(const MathUtil::Rectangle<int>& rc)
|
||||
{
|
||||
glScissor(rc.left, rc.top, rc.GetWidth(), rc.GetHeight());
|
||||
}
|
||||
|
||||
void OGLGfx::SetViewport(float x, float y, float width, float height, float near_depth,
|
||||
float far_depth)
|
||||
{
|
||||
if (g_ogl_config.bSupportViewportFloat)
|
||||
{
|
||||
glViewportIndexedf(0, x, y, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iceilf = [](float f) { return static_cast<GLint>(std::ceil(f)); };
|
||||
glViewport(iceilf(x), iceilf(y), iceilf(width), iceilf(height));
|
||||
}
|
||||
|
||||
glDepthRangef(near_depth, far_depth);
|
||||
}
|
||||
|
||||
void OGLGfx::Draw(u32 base_vertex, u32 num_vertices)
|
||||
{
|
||||
glDrawArrays(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(), base_vertex,
|
||||
num_vertices);
|
||||
}
|
||||
|
||||
void OGLGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
|
||||
{
|
||||
if (g_ogl_config.bSupportsGLBaseVertex)
|
||||
{
|
||||
glDrawElementsBaseVertex(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
|
||||
num_indices, GL_UNSIGNED_SHORT,
|
||||
static_cast<u16*>(nullptr) + base_index, base_vertex);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDrawElements(static_cast<const OGLPipeline*>(m_current_pipeline)->GetGLPrimitive(),
|
||||
num_indices, GL_UNSIGNED_SHORT, static_cast<u16*>(nullptr) + base_index);
|
||||
}
|
||||
}
|
||||
|
||||
void OGLGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
|
||||
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z)
|
||||
{
|
||||
glUseProgram(static_cast<const OGLShader*>(shader)->GetGLComputeProgramID());
|
||||
glDispatchCompute(groups_x, groups_y, groups_z);
|
||||
|
||||
// We messed up the program binding, so restore it.
|
||||
ProgramShaderCache::InvalidateLastProgram();
|
||||
if (m_current_pipeline)
|
||||
static_cast<const OGLPipeline*>(m_current_pipeline)->GetProgram()->shader.Bind();
|
||||
|
||||
// Barrier to texture can be used for reads.
|
||||
if (m_bound_image_texture)
|
||||
glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT);
|
||||
}
|
||||
|
||||
void OGLGfx::SelectLeftBuffer()
|
||||
{
|
||||
glDrawBuffer(GL_BACK_LEFT);
|
||||
}
|
||||
|
||||
void OGLGfx::SelectRightBuffer()
|
||||
{
|
||||
glDrawBuffer(GL_BACK_RIGHT);
|
||||
}
|
||||
|
||||
void OGLGfx::SelectMainBuffer()
|
||||
{
|
||||
glDrawBuffer(GL_BACK);
|
||||
}
|
||||
|
||||
void OGLGfx::SetFramebuffer(AbstractFramebuffer* framebuffer)
|
||||
{
|
||||
if (m_current_framebuffer == framebuffer)
|
||||
return;
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<OGLFramebuffer*>(framebuffer)->GetFBO());
|
||||
m_current_framebuffer = framebuffer;
|
||||
}
|
||||
|
||||
void OGLGfx::SetAndDiscardFramebuffer(AbstractFramebuffer* framebuffer)
|
||||
{
|
||||
// EXT_discard_framebuffer could be used here to save bandwidth on tilers.
|
||||
SetFramebuffer(framebuffer);
|
||||
}
|
||||
|
||||
void OGLGfx::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer, const ClearColor& color_value,
|
||||
float depth_value)
|
||||
{
|
||||
SetFramebuffer(framebuffer);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
GLbitfield clear_mask = 0;
|
||||
if (framebuffer->HasColorBuffer())
|
||||
{
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClearColor(color_value[0], color_value[1], color_value[2], color_value[3]);
|
||||
clear_mask |= GL_COLOR_BUFFER_BIT;
|
||||
}
|
||||
if (framebuffer->HasDepthBuffer())
|
||||
{
|
||||
glDepthMask(GL_TRUE);
|
||||
glClearDepthf(depth_value);
|
||||
clear_mask |= GL_DEPTH_BUFFER_BIT;
|
||||
}
|
||||
glClear(clear_mask);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
// Restore color/depth mask.
|
||||
if (framebuffer->HasColorBuffer())
|
||||
{
|
||||
glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
|
||||
m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
|
||||
}
|
||||
if (framebuffer->HasDepthBuffer())
|
||||
glDepthMask(m_current_depth_state.updateenable);
|
||||
}
|
||||
|
||||
void OGLGfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool colorEnable,
|
||||
bool alphaEnable, bool zEnable, u32 color, u32 z)
|
||||
{
|
||||
u32 clear_mask = 0;
|
||||
if (colorEnable || alphaEnable)
|
||||
{
|
||||
glColorMask(colorEnable, colorEnable, colorEnable, alphaEnable);
|
||||
glClearColor(float((color >> 16) & 0xFF) / 255.0f, float((color >> 8) & 0xFF) / 255.0f,
|
||||
float((color >> 0) & 0xFF) / 255.0f, float((color >> 24) & 0xFF) / 255.0f);
|
||||
clear_mask = GL_COLOR_BUFFER_BIT;
|
||||
}
|
||||
if (zEnable)
|
||||
{
|
||||
glDepthMask(zEnable ? GL_TRUE : GL_FALSE);
|
||||
glClearDepthf(float(z & 0xFFFFFF) / 16777216.0f);
|
||||
clear_mask |= GL_DEPTH_BUFFER_BIT;
|
||||
}
|
||||
|
||||
// Update rect for clearing the picture
|
||||
// glColorMask/glDepthMask/glScissor affect glClear (glViewport does not)
|
||||
g_gfx->SetScissorRect(target_rc);
|
||||
|
||||
glClear(clear_mask);
|
||||
|
||||
// Restore color/depth mask.
|
||||
if (colorEnable || alphaEnable)
|
||||
{
|
||||
glColorMask(m_current_blend_state.colorupdate, m_current_blend_state.colorupdate,
|
||||
m_current_blend_state.colorupdate, m_current_blend_state.alphaupdate);
|
||||
}
|
||||
if (zEnable)
|
||||
glDepthMask(m_current_depth_state.updateenable);
|
||||
}
|
||||
|
||||
void OGLGfx::BindBackbuffer(const ClearColor& clear_color)
|
||||
{
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
SetAndClearFramebuffer(m_system_framebuffer.get(), clear_color);
|
||||
}
|
||||
|
||||
void OGLGfx::PresentBackbuffer()
|
||||
{
|
||||
if (g_ogl_config.bSupportsDebug)
|
||||
{
|
||||
if (Common::Log::LogManager::GetInstance()->IsEnabled(Common::Log::LogType::HOST_GPU,
|
||||
Common::Log::LogLevel::LERROR))
|
||||
{
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_DEBUG_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
// Swap the back and front buffers, presenting the image.
|
||||
m_main_gl_context->Swap();
|
||||
}
|
||||
|
||||
void OGLGfx::OnConfigChanged(u32 bits)
|
||||
{
|
||||
AbstractGfx::OnConfigChanged(bits);
|
||||
|
||||
if (bits & CONFIG_CHANGE_BIT_VSYNC && !DriverDetails::HasBug(DriverDetails::BUG_BROKEN_VSYNC))
|
||||
m_main_gl_context->SwapInterval(g_ActiveConfig.bVSyncActive);
|
||||
|
||||
if (bits & CONFIG_CHANGE_BIT_ANISOTROPY)
|
||||
g_sampler_cache->Clear();
|
||||
}
|
||||
|
||||
void OGLGfx::Flush()
|
||||
{
|
||||
// ensure all commands are sent to the GPU.
|
||||
// Otherwise the driver could batch several frames together.
|
||||
glFlush();
|
||||
}
|
||||
|
||||
void OGLGfx::WaitForGPUIdle()
|
||||
{
|
||||
glFinish();
|
||||
}
|
||||
|
||||
void OGLGfx::CheckForSurfaceChange()
|
||||
{
|
||||
if (!g_presenter->SurfaceChangedTestAndClear())
|
||||
return;
|
||||
|
||||
m_main_gl_context->UpdateSurface(g_presenter->GetNewSurfaceHandle());
|
||||
|
||||
u32 width = m_main_gl_context->GetBackBufferWidth();
|
||||
u32 height = m_main_gl_context->GetBackBufferHeight();
|
||||
|
||||
// With a surface change, the window likely has new dimensions.
|
||||
g_presenter->SetBackbuffer(width, height);
|
||||
m_system_framebuffer->UpdateDimensions(width, height);
|
||||
}
|
||||
|
||||
void OGLGfx::CheckForSurfaceResize()
|
||||
{
|
||||
if (!g_presenter->SurfaceResizedTestAndClear())
|
||||
return;
|
||||
|
||||
m_main_gl_context->Update();
|
||||
u32 width = m_main_gl_context->GetBackBufferWidth();
|
||||
u32 height = m_main_gl_context->GetBackBufferHeight();
|
||||
g_presenter->SetBackbuffer(width, height);
|
||||
m_system_framebuffer->UpdateDimensions(width, height);
|
||||
}
|
||||
|
||||
void OGLGfx::BeginUtilityDrawing()
|
||||
{
|
||||
AbstractGfx::BeginUtilityDrawing();
|
||||
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
|
||||
{
|
||||
glDisable(GL_CLIP_DISTANCE0);
|
||||
glDisable(GL_CLIP_DISTANCE1);
|
||||
}
|
||||
}
|
||||
|
||||
void OGLGfx::EndUtilityDrawing()
|
||||
{
|
||||
AbstractGfx::EndUtilityDrawing();
|
||||
if (g_ActiveConfig.backend_info.bSupportsDepthClamp)
|
||||
{
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
glEnable(GL_CLIP_DISTANCE1);
|
||||
}
|
||||
}
|
||||
|
||||
void OGLGfx::ApplyRasterizationState(const RasterizationState state)
|
||||
{
|
||||
if (m_current_rasterization_state == state)
|
||||
return;
|
||||
|
||||
// none, ccw, cw, ccw
|
||||
if (state.cullmode != CullMode::None)
|
||||
{
|
||||
// TODO: GX_CULL_ALL not supported, yet!
|
||||
glEnable(GL_CULL_FACE);
|
||||
glFrontFace(state.cullmode == CullMode::Front ? GL_CCW : GL_CW);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
m_current_rasterization_state = state;
|
||||
}
|
||||
|
||||
void OGLGfx::ApplyDepthState(const DepthState state)
|
||||
{
|
||||
if (m_current_depth_state == state)
|
||||
return;
|
||||
|
||||
const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL,
|
||||
GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS};
|
||||
|
||||
if (state.testenable)
|
||||
{
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE);
|
||||
glDepthFunc(glCmpFuncs[u32(state.func.Value())]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the test is disabled write is disabled too
|
||||
// TODO: When PE performance metrics are being emulated via occlusion queries, we should
|
||||
// (probably?) enable depth test with depth function ALWAYS here
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
}
|
||||
|
||||
m_current_depth_state = state;
|
||||
}
|
||||
|
||||
void OGLGfx::ApplyBlendingState(const BlendingState state)
|
||||
{
|
||||
if (m_current_blend_state == state)
|
||||
return;
|
||||
|
||||
bool useDualSource = state.usedualsrc;
|
||||
|
||||
const GLenum src_factors[8] = {GL_ZERO,
|
||||
GL_ONE,
|
||||
GL_DST_COLOR,
|
||||
GL_ONE_MINUS_DST_COLOR,
|
||||
useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
|
||||
useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
|
||||
(GLenum)GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_DST_ALPHA,
|
||||
GL_ONE_MINUS_DST_ALPHA};
|
||||
const GLenum dst_factors[8] = {GL_ZERO,
|
||||
GL_ONE,
|
||||
GL_SRC_COLOR,
|
||||
GL_ONE_MINUS_SRC_COLOR,
|
||||
useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA,
|
||||
useDualSource ? GL_ONE_MINUS_SRC1_ALPHA :
|
||||
(GLenum)GL_ONE_MINUS_SRC_ALPHA,
|
||||
GL_DST_ALPHA,
|
||||
GL_ONE_MINUS_DST_ALPHA};
|
||||
|
||||
if (state.blendenable)
|
||||
glEnable(GL_BLEND);
|
||||
else
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
// Always call glBlendEquationSeparate and glBlendFuncSeparate, even when
|
||||
// GL_BLEND is disabled, as a workaround for some bugs (possibly graphics
|
||||
// driver issues?). See https://bugs.dolphin-emu.org/issues/10120 : "Sonic
|
||||
// Adventure 2 Battle: graphics crash when loading first Dark level"
|
||||
GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
||||
GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
||||
glBlendEquationSeparate(equation, equationAlpha);
|
||||
glBlendFuncSeparate(src_factors[u32(state.srcfactor.Value())],
|
||||
dst_factors[u32(state.dstfactor.Value())],
|
||||
src_factors[u32(state.srcfactoralpha.Value())],
|
||||
dst_factors[u32(state.dstfactoralpha.Value())]);
|
||||
|
||||
const GLenum logic_op_codes[16] = {
|
||||
GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP,
|
||||
GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE,
|
||||
GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET};
|
||||
|
||||
// Logic ops aren't available in GLES3
|
||||
if (!IsGLES())
|
||||
{
|
||||
if (state.logicopenable)
|
||||
{
|
||||
glEnable(GL_COLOR_LOGIC_OP);
|
||||
glLogicOp(logic_op_codes[u32(state.logicmode.Value())]);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
}
|
||||
}
|
||||
|
||||
glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate);
|
||||
m_current_blend_state = state;
|
||||
}
|
||||
|
||||
void OGLGfx::SetPipeline(const AbstractPipeline* pipeline)
|
||||
{
|
||||
if (m_current_pipeline == pipeline)
|
||||
return;
|
||||
|
||||
if (pipeline)
|
||||
{
|
||||
ApplyRasterizationState(static_cast<const OGLPipeline*>(pipeline)->GetRasterizationState());
|
||||
ApplyDepthState(static_cast<const OGLPipeline*>(pipeline)->GetDepthState());
|
||||
ApplyBlendingState(static_cast<const OGLPipeline*>(pipeline)->GetBlendingState());
|
||||
ProgramShaderCache::BindVertexFormat(
|
||||
static_cast<const OGLPipeline*>(pipeline)->GetVertexFormat());
|
||||
static_cast<const OGLPipeline*>(pipeline)->GetProgram()->shader.Bind();
|
||||
}
|
||||
else
|
||||
{
|
||||
ProgramShaderCache::InvalidateLastProgram();
|
||||
glUseProgram(0);
|
||||
}
|
||||
m_current_pipeline = pipeline;
|
||||
}
|
||||
|
||||
void OGLGfx::SetTexture(u32 index, const AbstractTexture* texture)
|
||||
{
|
||||
const OGLTexture* gl_texture = static_cast<const OGLTexture*>(texture);
|
||||
if (m_bound_textures[index] == gl_texture)
|
||||
return;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
if (gl_texture)
|
||||
glBindTexture(gl_texture->GetGLTarget(), gl_texture->GetGLTextureId());
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
m_bound_textures[index] = gl_texture;
|
||||
}
|
||||
|
||||
void OGLGfx::SetSamplerState(u32 index, const SamplerState& state)
|
||||
{
|
||||
g_sampler_cache->SetSamplerState(index, state);
|
||||
}
|
||||
|
||||
void OGLGfx::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write)
|
||||
{
|
||||
if (m_bound_image_texture == texture)
|
||||
return;
|
||||
|
||||
if (texture)
|
||||
{
|
||||
const GLenum access = read ? (write ? GL_READ_WRITE : GL_READ_ONLY) : GL_WRITE_ONLY;
|
||||
glBindImageTexture(0, static_cast<OGLTexture*>(texture)->GetGLTextureId(), 0, GL_TRUE, 0,
|
||||
access, static_cast<OGLTexture*>(texture)->GetGLFormatForImageTexture());
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
|
||||
}
|
||||
|
||||
m_bound_image_texture = texture;
|
||||
}
|
||||
|
||||
void OGLGfx::UnbindTexture(const AbstractTexture* texture)
|
||||
{
|
||||
for (size_t i = 0; i < m_bound_textures.size(); i++)
|
||||
{
|
||||
if (m_bound_textures[i] != texture)
|
||||
continue;
|
||||
|
||||
glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + i));
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
m_bound_textures[i] = nullptr;
|
||||
}
|
||||
|
||||
if (m_bound_image_texture == texture)
|
||||
{
|
||||
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
|
||||
m_bound_image_texture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<VideoCommon::AsyncShaderCompiler> OGLGfx::CreateAsyncShaderCompiler()
|
||||
{
|
||||
return std::make_unique<SharedContextAsyncShaderCompiler>();
|
||||
}
|
||||
|
||||
bool OGLGfx::IsGLES() const
|
||||
{
|
||||
return m_main_gl_context->IsGLES();
|
||||
}
|
||||
|
||||
void OGLGfx::BindSharedReadFramebuffer()
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_shared_read_framebuffer);
|
||||
}
|
||||
|
||||
void OGLGfx::BindSharedDrawFramebuffer()
|
||||
{
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_shared_draw_framebuffer);
|
||||
}
|
||||
|
||||
void OGLGfx::RestoreFramebufferBinding()
|
||||
{
|
||||
glBindFramebuffer(
|
||||
GL_FRAMEBUFFER,
|
||||
m_current_framebuffer ? static_cast<OGLFramebuffer*>(m_current_framebuffer)->GetFBO() : 0);
|
||||
}
|
||||
|
||||
SurfaceInfo OGLGfx::GetSurfaceInfo() const
|
||||
{
|
||||
return {std::max(m_main_gl_context->GetBackBufferWidth(), 1u),
|
||||
std::max(m_main_gl_context->GetBackBufferHeight(), 1u), m_backbuffer_scale,
|
||||
AbstractTextureFormat::RGBA8};
|
||||
}
|
||||
|
||||
} // namespace OGL
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2022, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "../../dolphin/Source/Core/VideoCommon/TextureDecoder_x64.cpp"
|
||||
#endif
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2022, OpenEmu Team
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "../../dolphin/Source/Core/VideoCommon/VertexLoaderX64.cpp"
|
||||
#elif defined(__arm64__)
|
||||
#include "../../dolphin/Source/Core/VideoCommon/VertexLoaderARM64.cpp"
|
||||
#endif
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
enum class APIType;
|
||||
|
||||
@@ -55,80 +53,43 @@ enum class ShaderCompilationMode : int
|
||||
AsynchronousSkipRendering
|
||||
};
|
||||
|
||||
enum class TextureFilteringMode : int
|
||||
{
|
||||
Default,
|
||||
Nearest,
|
||||
Linear,
|
||||
};
|
||||
|
||||
enum class TriState : int
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
Auto
|
||||
};
|
||||
|
||||
// Bitmask containing information about which configuration has changed for the backend.
|
||||
enum ConfigChangeBits : u32
|
||||
{
|
||||
CONFIG_CHANGE_BIT_HOST_CONFIG = (1 << 0),
|
||||
CONFIG_CHANGE_BIT_MULTISAMPLES = (1 << 1),
|
||||
CONFIG_CHANGE_BIT_STEREO_MODE = (1 << 2),
|
||||
CONFIG_CHANGE_BIT_TARGET_SIZE = (1 << 3),
|
||||
CONFIG_CHANGE_BIT_ANISOTROPY = (1 << 4),
|
||||
CONFIG_CHANGE_BIT_FORCE_TEXTURE_FILTERING = (1 << 5),
|
||||
CONFIG_CHANGE_BIT_VSYNC = (1 << 6),
|
||||
CONFIG_CHANGE_BIT_BBOX = (1 << 7),
|
||||
CONFIG_CHANGE_BIT_ASPECT_RATIO = (1 << 8),
|
||||
CONFIG_CHANGE_BIT_POST_PROCESSING_SHADER = (1 << 9),
|
||||
};
|
||||
|
||||
// NEVER inherit from this class.
|
||||
struct VideoConfig final
|
||||
{
|
||||
VideoConfig() = default;
|
||||
VideoConfig();
|
||||
void Refresh();
|
||||
void VerifyValidity();
|
||||
|
||||
// General
|
||||
bool bVSync = false;
|
||||
bool bVSyncActive = false;
|
||||
bool bWidescreenHack = false;
|
||||
AspectMode aspect_mode{};
|
||||
AspectMode suggested_aspect_mode{};
|
||||
bool bCrop = false; // Aspect ratio controls.
|
||||
bool bShaderCache = false;
|
||||
bool bVSync;
|
||||
bool bVSyncActive;
|
||||
bool bWidescreenHack;
|
||||
AspectMode aspect_mode;
|
||||
AspectMode suggested_aspect_mode;
|
||||
bool bCrop; // Aspect ratio controls.
|
||||
bool bShaderCache;
|
||||
|
||||
// Enhancements
|
||||
u32 iMultisamples = 0;
|
||||
bool bSSAA = false;
|
||||
int iEFBScale = 0;
|
||||
TextureFilteringMode texture_filtering_mode = TextureFilteringMode::Default;
|
||||
int iMaxAnisotropy = 0;
|
||||
u32 iMultisamples;
|
||||
bool bSSAA;
|
||||
int iEFBScale;
|
||||
bool bForceFiltering;
|
||||
int iMaxAnisotropy;
|
||||
std::string sPostProcessingShader;
|
||||
bool bForceTrueColor = false;
|
||||
bool bDisableCopyFilter = false;
|
||||
bool bArbitraryMipmapDetection = false;
|
||||
float fArbitraryMipmapDetectionThreshold = 0;
|
||||
bool bForceTrueColor;
|
||||
bool bDisableCopyFilter;
|
||||
bool bArbitraryMipmapDetection;
|
||||
float fArbitraryMipmapDetectionThreshold;
|
||||
|
||||
// Information
|
||||
bool bShowFPS = false;
|
||||
bool bShowFTimes = false;
|
||||
bool bShowVPS = false;
|
||||
bool bShowVTimes = false;
|
||||
bool bShowGraphs = false;
|
||||
bool bShowSpeed = false;
|
||||
bool bShowSpeedColors = false;
|
||||
int iPerfSampleUSec = 0;
|
||||
bool bShowNetPlayPing = false;
|
||||
bool bShowNetPlayMessages = false;
|
||||
bool bOverlayStats = false;
|
||||
bool bOverlayProjStats = false;
|
||||
bool bOverlayScissorStats = false;
|
||||
bool bTexFmtOverlayEnable = false;
|
||||
bool bTexFmtOverlayCenter = false;
|
||||
bool bLogRenderTimeToFile = false;
|
||||
bool bShowFPS;
|
||||
bool bShowNetPlayPing;
|
||||
bool bShowNetPlayMessages;
|
||||
bool bOverlayStats;
|
||||
bool bOverlayProjStats;
|
||||
bool bTexFmtOverlayEnable;
|
||||
bool bTexFmtOverlayCenter;
|
||||
bool bLogRenderTimeToFile;
|
||||
|
||||
// Render
|
||||
bool bWireFrame;
|
||||
@@ -138,101 +99,94 @@ struct VideoConfig final
|
||||
int iRenderFBO = 0;
|
||||
|
||||
// Utility
|
||||
bool bDumpTextures = false;
|
||||
bool bDumpMipmapTextures = false;
|
||||
bool bDumpBaseTextures = false;
|
||||
bool bHiresTextures = false;
|
||||
bool bCacheHiresTextures = false;
|
||||
bool bDumpEFBTarget = false;
|
||||
bool bDumpXFBTarget = false;
|
||||
bool bDumpFramesAsImages = false;
|
||||
bool bUseFFV1 = false;
|
||||
bool bDumpTextures;
|
||||
bool bDumpMipmapTextures;
|
||||
bool bDumpBaseTextures;
|
||||
bool bHiresTextures;
|
||||
bool bCacheHiresTextures;
|
||||
bool bDumpEFBTarget;
|
||||
bool bDumpXFBTarget;
|
||||
bool bDumpFramesAsImages;
|
||||
bool bUseFFV1;
|
||||
std::string sDumpCodec;
|
||||
std::string sDumpPixelFormat;
|
||||
std::string sDumpEncoder;
|
||||
std::string sDumpFormat;
|
||||
std::string sDumpPath;
|
||||
bool bInternalResolutionFrameDumps = false;
|
||||
bool bBorderlessFullscreen = false;
|
||||
bool bEnableGPUTextureDecoding = false;
|
||||
bool bPreferVSForLinePointExpansion = false;
|
||||
int iBitrateKbps = 0;
|
||||
bool bGraphicMods = false;
|
||||
std::optional<GraphicsModGroupConfig> graphics_mod_config;
|
||||
bool bInternalResolutionFrameDumps;
|
||||
bool bBorderlessFullscreen;
|
||||
bool bEnableGPUTextureDecoding;
|
||||
int iBitrateKbps;
|
||||
|
||||
// Hacks
|
||||
bool bEFBAccessEnable = false;
|
||||
bool bEFBAccessDeferInvalidation = false;
|
||||
bool bPerfQueriesEnable = false;
|
||||
bool bBBoxEnable = false;
|
||||
bool bForceProgressive = false;
|
||||
bool bCPUCull = false;
|
||||
bool bEFBAccessEnable;
|
||||
bool bEFBAccessDeferInvalidation;
|
||||
bool bPerfQueriesEnable;
|
||||
bool bBBoxEnable;
|
||||
bool bForceProgressive;
|
||||
|
||||
bool bEFBEmulateFormatChanges = false;
|
||||
bool bSkipEFBCopyToRam = false;
|
||||
bool bSkipXFBCopyToRam = false;
|
||||
bool bDisableCopyToVRAM = false;
|
||||
bool bDeferEFBCopies = false;
|
||||
bool bImmediateXFB = false;
|
||||
bool bSkipPresentingDuplicateXFBs = false;
|
||||
bool bCopyEFBScaled = false;
|
||||
int iSafeTextureCache_ColorSamples = 0;
|
||||
float fAspectRatioHackW = 1; // Initial value needed for the first frame
|
||||
float fAspectRatioHackH = 1;
|
||||
bool bEnablePixelLighting = false;
|
||||
bool bFastDepthCalc = false;
|
||||
bool bVertexRounding = false;
|
||||
bool bVISkip = false;
|
||||
int iEFBAccessTileSize = 0;
|
||||
int iSaveTargetId = 0; // TODO: Should be dropped
|
||||
u32 iMissingColorValue = 0;
|
||||
bool bFastTextureSampling = false;
|
||||
#ifdef __APPLE__
|
||||
bool bNoMipmapping = false; // Used by macOS fifoci to work around an M1 bug
|
||||
#endif
|
||||
bool bEFBEmulateFormatChanges;
|
||||
bool bSkipEFBCopyToRam;
|
||||
bool bSkipXFBCopyToRam;
|
||||
bool bDisableCopyToVRAM;
|
||||
bool bDeferEFBCopies;
|
||||
bool bImmediateXFB;
|
||||
bool bSkipPresentingDuplicateXFBs;
|
||||
bool bCopyEFBScaled;
|
||||
int iSafeTextureCache_ColorSamples;
|
||||
float fAspectRatioHackW, fAspectRatioHackH;
|
||||
bool bEnablePixelLighting;
|
||||
bool bFastDepthCalc;
|
||||
bool bVertexRounding;
|
||||
int iEFBAccessTileSize;
|
||||
int iLog; // CONF_ bits
|
||||
int iSaveTargetId; // TODO: Should be dropped
|
||||
|
||||
// Stereoscopy
|
||||
StereoMode stereo_mode{};
|
||||
int iStereoDepth = 0;
|
||||
int iStereoConvergence = 0;
|
||||
int iStereoConvergencePercentage = 0;
|
||||
bool bStereoSwapEyes = false;
|
||||
bool bStereoEFBMonoDepth = false;
|
||||
int iStereoDepthPercentage = 0;
|
||||
StereoMode stereo_mode;
|
||||
int iStereoDepth;
|
||||
int iStereoConvergence;
|
||||
int iStereoConvergencePercentage;
|
||||
bool bStereoSwapEyes;
|
||||
bool bStereoEFBMonoDepth;
|
||||
int iStereoDepthPercentage;
|
||||
|
||||
// D3D only config, mostly to be merged into the above
|
||||
int iAdapter = 0;
|
||||
int iAdapter;
|
||||
|
||||
// Metal only config
|
||||
TriState iManuallyUploadBuffers = TriState::Auto;
|
||||
TriState iUsePresentDrawable = TriState::Auto;
|
||||
// VideoSW Debugging
|
||||
int drawStart;
|
||||
int drawEnd;
|
||||
bool bZComploc;
|
||||
bool bZFreeze;
|
||||
bool bDumpObjects;
|
||||
bool bDumpTevStages;
|
||||
bool bDumpTevTextureFetches;
|
||||
|
||||
// Enable API validation layers, currently only supported with Vulkan.
|
||||
bool bEnableValidationLayer = false;
|
||||
bool bEnableValidationLayer;
|
||||
|
||||
// Multithreaded submission, currently only supported with Vulkan.
|
||||
bool bBackendMultithreading = true;
|
||||
bool bBackendMultithreading;
|
||||
|
||||
// Early command buffer execution interval in number of draws.
|
||||
// Currently only supported with Vulkan.
|
||||
int iCommandBufferExecuteInterval = 0;
|
||||
int iCommandBufferExecuteInterval;
|
||||
|
||||
// Shader compilation settings.
|
||||
bool bWaitForShadersBeforeStarting = false;
|
||||
ShaderCompilationMode iShaderCompilationMode{};
|
||||
bool bWaitForShadersBeforeStarting;
|
||||
ShaderCompilationMode iShaderCompilationMode;
|
||||
|
||||
// Number of shader compiler threads.
|
||||
// 0 disables background compilation.
|
||||
// -1 uses an automatic number based on the CPU threads.
|
||||
int iShaderCompilerThreads = 0;
|
||||
int iShaderPrecompilerThreads = 0;
|
||||
int iShaderCompilerThreads;
|
||||
int iShaderPrecompilerThreads;
|
||||
|
||||
// Static config per API
|
||||
// TODO: Move this out of VideoConfig
|
||||
struct
|
||||
{
|
||||
APIType api_type = APIType::Nothing;
|
||||
std::string DisplayName;
|
||||
APIType api_type;
|
||||
|
||||
std::vector<std::string> Adapters; // for D3D
|
||||
std::vector<u32> AAModes;
|
||||
@@ -240,62 +194,45 @@ struct VideoConfig final
|
||||
// TODO: merge AdapterName and Adapters array
|
||||
std::string AdapterName; // for OpenGL
|
||||
|
||||
u32 MaxTextureSize = 16384;
|
||||
bool bUsesLowerLeftOrigin = false;
|
||||
bool bUsesExplictQuadBuffering = false;
|
||||
u32 MaxTextureSize;
|
||||
bool bUsesLowerLeftOrigin;
|
||||
|
||||
bool bSupportsExclusiveFullscreen = false;
|
||||
bool bSupportsDualSourceBlend = false;
|
||||
bool bSupportsPrimitiveRestart = false;
|
||||
bool bSupportsGeometryShaders = false;
|
||||
bool bSupportsComputeShaders = false;
|
||||
bool bSupports3DVision = false;
|
||||
bool bSupportsEarlyZ = false; // needed by PixelShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsBindingLayout = false; // Needed by ShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsBBox = false;
|
||||
bool bSupportsGSInstancing = false; // Needed by GeometryShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsPostProcessing = false;
|
||||
bool bSupportsPaletteConversion = false;
|
||||
bool bSupportsClipControl = false; // Needed by VertexShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsSSAA = false;
|
||||
bool bSupportsFragmentStoresAndAtomics = false; // a.k.a. OpenGL SSBOs a.k.a. Direct3D UAVs
|
||||
bool bSupportsDepthClamp = false; // Needed by VertexShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsReversedDepthRange = false;
|
||||
bool bSupportsLogicOp = false;
|
||||
bool bSupportsMultithreading = false;
|
||||
bool bSupportsGPUTextureDecoding = false;
|
||||
bool bSupportsST3CTextures = false;
|
||||
bool bSupportsCopyToVram = false;
|
||||
bool bSupportsBitfield = false; // Needed by UberShaders, so must stay in VideoCommon
|
||||
// Needed by UberShaders, so must stay in VideoCommon
|
||||
bool bSupportsDynamicSamplerIndexing = false;
|
||||
bool bSupportsBPTCTextures = false;
|
||||
bool bSupportsFramebufferFetch = false; // Used as an alternative to dual-source blend on GLES
|
||||
bool bSupportsBackgroundCompiling = false;
|
||||
bool bSupportsLargePoints = false;
|
||||
bool bSupportsPartialDepthCopies = false;
|
||||
bool bSupportsDepthReadback = false;
|
||||
bool bSupportsShaderBinaries = false;
|
||||
bool bSupportsPipelineCacheData = false;
|
||||
bool bSupportsCoarseDerivatives = false;
|
||||
bool bSupportsTextureQueryLevels = false;
|
||||
bool bSupportsLodBiasInSampler = false;
|
||||
bool bSupportsSettingObjectNames = false;
|
||||
bool bSupportsPartialMultisampleResolve = false;
|
||||
bool bSupportsDynamicVertexLoader = false;
|
||||
bool bSupportsVSLinePointExpand = false;
|
||||
bool bSupportsGLLayerInFS = true;
|
||||
bool bSupportsExclusiveFullscreen;
|
||||
bool bSupportsDualSourceBlend;
|
||||
bool bSupportsPrimitiveRestart;
|
||||
bool bSupportsOversizedViewports;
|
||||
bool bSupportsGeometryShaders;
|
||||
bool bSupportsComputeShaders;
|
||||
bool bSupports3DVision;
|
||||
bool bSupportsEarlyZ; // needed by PixelShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsBindingLayout; // Needed by ShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsBBox;
|
||||
bool bSupportsGSInstancing; // Needed by GeometryShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsPostProcessing;
|
||||
bool bSupportsPaletteConversion;
|
||||
bool bSupportsClipControl; // Needed by VertexShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsSSAA;
|
||||
bool bSupportsFragmentStoresAndAtomics; // a.k.a. OpenGL SSBOs a.k.a. Direct3D UAVs
|
||||
bool bSupportsDepthClamp; // Needed by VertexShaderGen, so must stay in VideoCommon
|
||||
bool bSupportsReversedDepthRange;
|
||||
bool bSupportsLogicOp;
|
||||
bool bSupportsMultithreading;
|
||||
bool bSupportsGPUTextureDecoding;
|
||||
bool bSupportsST3CTextures;
|
||||
bool bSupportsCopyToVram;
|
||||
bool bSupportsBitfield; // Needed by UberShaders, so must stay in VideoCommon
|
||||
bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon
|
||||
bool bSupportsBPTCTextures;
|
||||
bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES
|
||||
bool bSupportsBackgroundCompiling;
|
||||
bool bSupportsLargePoints;
|
||||
bool bSupportsPartialDepthCopies;
|
||||
bool bSupportsDepthReadback;
|
||||
bool bSupportsShaderBinaries;
|
||||
bool bSupportsPipelineCacheData;
|
||||
} backend_info;
|
||||
|
||||
// Utility
|
||||
bool UseVSForLinePointExpand() const
|
||||
{
|
||||
if (!backend_info.bSupportsVSLinePointExpand)
|
||||
return false;
|
||||
if (!backend_info.bSupportsGeometryShaders)
|
||||
return true;
|
||||
return bPreferVSForLinePointExpansion;
|
||||
}
|
||||
bool MultisamplingEnabled() const { return iMultisamples > 1; }
|
||||
bool ExclusiveFullscreenEnabled() const
|
||||
{
|
||||
@@ -306,26 +243,6 @@ struct VideoConfig final
|
||||
return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding;
|
||||
}
|
||||
bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; }
|
||||
bool ManualTextureSamplingWithCustomTextureSizes() const
|
||||
{
|
||||
// If manual texture sampling is disabled, we don't need to do anything.
|
||||
if (bFastTextureSampling)
|
||||
return false;
|
||||
// Hi-res textures break the wrapping logic used by manual texture sampling, as a texture's
|
||||
// size won't match the size the game sets.
|
||||
if (bHiresTextures)
|
||||
return true;
|
||||
// Hi-res EFB copies (but not native-resolution EFB copies at higher internal resolutions)
|
||||
// also result in different texture sizes that need special handling.
|
||||
if (iEFBScale != 1 && bCopyEFBScaled)
|
||||
return true;
|
||||
// Stereoscopic 3D changes the number of layers some textures have (EFB copies have 2 layers,
|
||||
// while game textures still have 1), meaning bounds checks need to be added.
|
||||
if (stereo_mode != StereoMode::Off)
|
||||
return true;
|
||||
// Otherwise, manual texture sampling can use the sizes games specify directly.
|
||||
return false;
|
||||
}
|
||||
bool UsingUberShaders() const;
|
||||
u32 GetShaderCompilerThreads() const;
|
||||
u32 GetShaderPrecompilerThreads() const;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Default visibility */
|
||||
#define DEFAULT_VISIBILITY __attribute__((visibility("hidden")))
|
||||
#define DEFAULT_VISIBILITY __attribute__((visibility("default")))
|
||||
|
||||
/* Start with debug message logging enabled */
|
||||
#undef ENABLE_DEBUG_LOGGING
|
||||
|
||||
@@ -281,7 +281,7 @@
|
||||
|
||||
/* Define to 1 or 0, depending whether the compiler supports simple visibility
|
||||
declarations. */
|
||||
#define HAVE_VISIBILITY 0
|
||||
#define HAVE_VISIBILITY 1
|
||||
|
||||
/* Define to 1 if you have the `wcwidth' function. */
|
||||
#define HAVE_WCWIDTH 1
|
||||
|
||||
@@ -32,7 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/ARDecrypt.h"
|
||||
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
#include "InputCommon/ControllerInterface/Device.h"
|
||||
|
||||
#include "DolphinNoGUI/Platform.h"
|
||||
|
||||
@@ -59,8 +59,8 @@ public:
|
||||
void SetIR(int player, float x, float y);
|
||||
|
||||
void processSpecialKeys (int button , int player);
|
||||
void setWiimoteSideways (int player);
|
||||
void setWiimoteUpright (int player);
|
||||
void setWiimoteSideways (int player, bool sideways);
|
||||
void setWiimoteUpright (int player, bool upright);
|
||||
void changeWiimoteExtension(int extension, int player);
|
||||
|
||||
void toggleAudioMute();
|
||||
|
||||
+33
-52
@@ -44,14 +44,12 @@
|
||||
#include "Common/Thread.h"
|
||||
#include "Common/Version.h"
|
||||
|
||||
#include "Core/DolphinAnalytics.h"
|
||||
#include "Core/Analytics.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/BootManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/WiimoteSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
@@ -80,9 +78,6 @@
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoBackends/OGL/ProgramShaderCache.h"
|
||||
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
DolHost* DolHost::m_instance = nullptr;
|
||||
static Common::Event updateMainFrameEvent;
|
||||
static Common::Flag s_running{true};
|
||||
@@ -116,33 +111,33 @@ void DolHost::Init(std::string supportDirectoryPath, std::string cpath)
|
||||
UICommon::Init();
|
||||
|
||||
// Database Settings
|
||||
Config::SetBase(Config::MAIN_USE_BUILT_IN_TITLE_DATABASE, true);
|
||||
SConfig::GetInstance().m_use_builtin_title_database = true;
|
||||
|
||||
//Setup the CPU Settings
|
||||
Config::SetBase(Config::MAIN_MMU, true);
|
||||
Config::SetBase(Config::MAIN_ENABLE_CHEATS, true);
|
||||
SConfig::GetInstance().bMMU = true;
|
||||
SConfig::GetInstance().bEnableCheats = true;
|
||||
SConfig::GetInstance().bBootToPause = false;
|
||||
|
||||
//Debug Settings
|
||||
Config::SetBase(Config::MAIN_ENABLE_DEBUGGING, false);
|
||||
SConfig::GetInstance().bEnableDebugging = false;
|
||||
#ifdef DEBUG
|
||||
Config::SetBase(Config::MAIN_OSD_MESSAGES, true);
|
||||
#else
|
||||
Config::SetBase(Config::MAIN_OSD_MESSAGES, false);
|
||||
#endif
|
||||
Config::SetBase(Config::MAIN_SHOW_FRAME_COUNT, false);
|
||||
SConfig::GetInstance().m_ShowFrameCount = false;
|
||||
|
||||
//Video
|
||||
Config::SetBase(Config::MAIN_GFX_BACKEND, "MTL");
|
||||
Config::SetBase(Config::MAIN_GFX_BACKEND, "OGL");
|
||||
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
|
||||
|
||||
//Set the Sound
|
||||
Config::SetBase(Config::MAIN_DSP_HLE, true);
|
||||
Config::SetBase(Config::MAIN_DSP_THREAD, true);
|
||||
Config::SetBase(Config::MAIN_AUDIO_VOLUME, 0);
|
||||
SConfig::GetInstance().bDSPHLE = true;
|
||||
SConfig::GetInstance().bDSPThread = true;
|
||||
SConfig::GetInstance().m_Volume = 0;
|
||||
|
||||
//Split CPU thread from GPU
|
||||
Config::SetBase(Config::MAIN_CPU_THREAD, true);
|
||||
SConfig::GetInstance().bCPUThread = true;
|
||||
|
||||
//Analitics
|
||||
Config::SetBase(Config::MAIN_ANALYTICS_PERMISSION_ASKED, true);
|
||||
@@ -153,10 +148,10 @@ void DolHost::Init(std::string supportDirectoryPath, std::string cpath)
|
||||
SConfig::GetInstance().SaveSettings();
|
||||
|
||||
//Choose Wiimote Type
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(0), WiimoteSource::Emulated);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(1), WiimoteSource::Emulated);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(2), WiimoteSource::Emulated);
|
||||
Config::SetCurrent(Config::GetInfoForWiimoteSource(3), WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(0, WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(1, WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(2, WiimoteSource::Emulated);
|
||||
WiimoteCommon::SetSource(3, WiimoteSource::Emulated);
|
||||
|
||||
//Get game info from file path
|
||||
GetGameInfo();
|
||||
@@ -194,7 +189,7 @@ void DolHost::Init(std::string supportDirectoryPath, std::string cpath)
|
||||
// SConfig::GetInstance().m_enable_signature_checks = false;
|
||||
|
||||
// Disable wiimote continuous scanning
|
||||
Config::SetBase(Config::MAIN_WIIMOTE_CONTINUOUS_SCANNING, false);
|
||||
SConfig::GetInstance().m_WiimoteContinuousScanning = false;
|
||||
|
||||
//Set the Wiimote type
|
||||
// WiimoteReal::ChangeWiimoteSource(0, _wiiMoteType);
|
||||
@@ -207,7 +202,7 @@ void DolHost::Init(std::string supportDirectoryPath, std::string cpath)
|
||||
# pragma mark - Execution
|
||||
bool DolHost::LoadFileAtPath()
|
||||
{
|
||||
Core::AddOnStateChangedCallback([](Core::State state) {
|
||||
Core::SetOnStateChangedCallback([](Core::State state) {
|
||||
if (state == Core::State::Uninitialized)
|
||||
s_running.Clear();
|
||||
});
|
||||
@@ -250,7 +245,7 @@ void DolHost::Pause(bool flag)
|
||||
void DolHost::RequestStop()
|
||||
{
|
||||
Core::SetState(Core::State::Running);
|
||||
Core::System::GetInstance().GetProcessorInterface().PowerButton_Tap();
|
||||
ProcessorInterface::PowerButton_Tap();
|
||||
|
||||
Core::Stop();
|
||||
while (CPU::GetState() != CPU::State::PowerDown)
|
||||
@@ -262,7 +257,7 @@ void DolHost::RequestStop()
|
||||
|
||||
void DolHost::Reset()
|
||||
{
|
||||
Core::System::GetInstance().GetProcessorInterface().ResetButton_Tap();
|
||||
ProcessorInterface::ResetButton_Tap();
|
||||
}
|
||||
|
||||
void DolHost::UpdateFrame()
|
||||
@@ -296,8 +291,8 @@ void DolHost::SetBackBufferSize(int width, int height) {
|
||||
# pragma mark - Audio
|
||||
void DolHost::SetVolume(float value)
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::MAIN_AUDIO_VOLUME, value * 100);
|
||||
AudioCommon::UpdateSoundStream(Core::System::GetInstance());
|
||||
SConfig::GetInstance().m_Volume = value * 100;
|
||||
AudioCommon::UpdateSoundStream();
|
||||
}
|
||||
|
||||
# pragma mark - Save states
|
||||
@@ -461,9 +456,7 @@ void DolHost::SetCheat(std::string code, std::string type, bool enabled)
|
||||
if(!exists)
|
||||
arcodes.push_back(arcode);
|
||||
|
||||
// TODO: move to a better place
|
||||
Core::CPUThreadGuard guard;
|
||||
ActionReplay::RunAllActive(guard);
|
||||
ActionReplay::RunAllActive();
|
||||
}
|
||||
|
||||
# pragma mark - Controls
|
||||
@@ -498,29 +491,32 @@ void DolHost::SetAxis(int button, float value, int player)
|
||||
|
||||
void DolHost::processSpecialKeys (int button , int player)
|
||||
{
|
||||
player -= 1;
|
||||
button += 1;
|
||||
|
||||
if (button == OEWiimoteSideways) {
|
||||
_wmSideways[player] = !_wmSideways[player];
|
||||
setWiimoteSideways(player);
|
||||
setWiimoteSideways(player, _wmSideways[player - 1]);
|
||||
} else if (button == OEWiimoteUpright) {
|
||||
_wmUpright[player] = !_wmUpright[player];
|
||||
setWiimoteUpright(player);
|
||||
setWiimoteUpright(player ,_wmUpright[player - 1]);
|
||||
}
|
||||
}
|
||||
void DolHost::setWiimoteSideways (int player)
|
||||
|
||||
void DolHost::setWiimoteSideways (int player, bool sideways)
|
||||
{
|
||||
static_cast<ControllerEmu::NumericSetting<bool>*>(Wiimote::GetWiimoteGroup(player, WiimoteEmu::WiimoteGroup::Options)->numeric_settings[3].get())->SetValue(_wmSideways[player]);
|
||||
player -= 1;
|
||||
static_cast<ControllerEmu::NumericSetting<bool>*>(Wiimote::GetWiimoteGroup(player, WiimoteEmu::WiimoteGroup::Options)->numeric_settings[3].get())->SetValue(sideways);
|
||||
}
|
||||
|
||||
void DolHost::setWiimoteUpright (int player)
|
||||
void DolHost::setWiimoteUpright (int player, bool upright)
|
||||
{
|
||||
static_cast<ControllerEmu::NumericSetting<bool>*>(Wiimote::GetWiimoteGroup(player, WiimoteEmu::WiimoteGroup::Options)->numeric_settings[2].get())->SetValue(_wmUpright[player]);
|
||||
player -= 1;
|
||||
static_cast<ControllerEmu::NumericSetting<bool>*>(Wiimote::GetWiimoteGroup(player, WiimoteEmu::WiimoteGroup::Options)->numeric_settings[2].get())->SetValue(upright);
|
||||
}
|
||||
|
||||
void DolHost::changeWiimoteExtension(int extension, int player)
|
||||
{
|
||||
|
||||
player -= 1;
|
||||
static_cast<ControllerEmu::Attachments*>(Wiimote::GetWiimoteGroup(player, WiimoteEmu::WiimoteGroup::Attachments))->SetSelectedAttachment(extension);
|
||||
}
|
||||
@@ -619,30 +615,15 @@ void Host_RequestRenderWindowSize(int width, int height){}
|
||||
void Host_SetStartupDebuggingParameters()
|
||||
{
|
||||
SConfig& StartUp = SConfig::GetInstance();
|
||||
StartUp.bEnableDebugging = false;
|
||||
StartUp.bBootToPause = false;
|
||||
}
|
||||
|
||||
std::vector<std::string> Host_GetPreferredLocales()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Host_UINeedsControllerState(){ return false; }
|
||||
bool Host_UIBlocksControllerState() { return false; }
|
||||
bool Host_RendererHasFocus() { return true; }
|
||||
bool Host_RendererHasFullFocus() { return true; }
|
||||
bool Host_RendererIsFullscreen() { return false; }
|
||||
void Host_ShowVideoConfig(void*, const std::string&) {}
|
||||
void Host_YieldToUI() {}
|
||||
void Host_TitleChanged() {}
|
||||
void Host_UpdateProgressDialog(const char* caption, int position, int total) {}
|
||||
|
||||
void Host_UpdateDiscordClientID(const std::string& client_id) {}
|
||||
bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state,
|
||||
const std::string& large_image_key,
|
||||
const std::string& large_image_text,
|
||||
const std::string& small_image_key,
|
||||
const std::string& small_image_text,
|
||||
const int64_t start_timestamp,
|
||||
const int64_t end_timestamp, const int party_size,
|
||||
const int party_max) { return false; }
|
||||
|
||||
+274
-2
@@ -71,6 +71,8 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
NSString *_dolphinCoreModule;
|
||||
OEIntSize _dolphinCoreAspect;
|
||||
OEIntSize _dolphinCoreScreen;
|
||||
|
||||
NSMutableArray <NSMutableDictionary <NSString *, id> *> *_advancedMenus;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@@ -145,6 +147,9 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
|
||||
_frameInterval = dol_host->GetFrameInterval();
|
||||
|
||||
if (_isWii)
|
||||
[self changeAdvancedMenuOption:@"Wiimote" menuID:@"Controller1"];
|
||||
|
||||
}
|
||||
[super startEmulation];
|
||||
|
||||
@@ -182,7 +187,7 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
# pragma mark - Video
|
||||
- (OEGameCoreRendering)gameCoreRendering
|
||||
{
|
||||
return OEGameCoreRenderingMetal2Video;
|
||||
return OEGameCoreRenderingOpenGL3Video;
|
||||
}
|
||||
|
||||
- (BOOL)hasAlternateRenderingThread
|
||||
@@ -226,7 +231,7 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
|
||||
- (GLenum)pixelType
|
||||
{
|
||||
return GL_UNSIGNED_INT_8_8_8_8_REV;
|
||||
return GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
- (GLenum)internalPixelFormat
|
||||
@@ -407,4 +412,271 @@ extern std::unique_ptr<SoundStream> g_sound_stream;
|
||||
{
|
||||
dol_host->SetCheat([code UTF8String], [type UTF8String], enabled);
|
||||
}
|
||||
|
||||
# pragma mark - Advanced Menu Functions (do not change)
|
||||
- (void)changeAdvancedMenuOption:(NSString *)advancedMenuName menuID:(NSString *)menuID
|
||||
{
|
||||
if (_advancedMenus.count == 0)
|
||||
[self advancedMenu];
|
||||
|
||||
// First check if Menu Option is toggleable and grab its preference key
|
||||
BOOL isMenuToggleable = NO;
|
||||
BOOL isValidMenu = NO;
|
||||
BOOL menuState = NO;
|
||||
NSString *menuPrefKey;
|
||||
NSString *menuIDKey;
|
||||
|
||||
for (NSDictionary *menuDict in _advancedMenus)
|
||||
{
|
||||
if (menuDict[OEGameCoreAdvancedMenuGroupNameKey]){
|
||||
NSDictionary *subMenuDict = [self findAdvancedSubMenu:menuDict menuName:advancedMenuName menuID:menuID];
|
||||
if (subMenuDict){
|
||||
menuState = [subMenuDict[OEGameCoreAdvancedMenuStateKey] boolValue];
|
||||
menuPrefKey = subMenuDict[OEGameCoreAdvancedMenuPrefKeyNameKey];
|
||||
menuIDKey = subMenuDict[OEGameCoreAdvancedMenuGroupIDKey];
|
||||
isMenuToggleable = [subMenuDict[OEGameCoreAdvancedMenuAllowsToggleKey] boolValue];
|
||||
isValidMenu = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ([menuDict[OEGameCoreAdvancedMenuNameKey] isEqualToString:advancedMenuName])
|
||||
{
|
||||
if ([menuDict[OEGameCoreAdvancedMenuGroupIDKey] isEqualToString:menuID])
|
||||
{
|
||||
menuState = [menuDict[OEGameCoreAdvancedMenuStateKey] boolValue];
|
||||
menuPrefKey = menuDict[OEGameCoreAdvancedMenuPrefKeyNameKey];
|
||||
menuIDKey = menuDict[OEGameCoreAdvancedMenuGroupIDKey];
|
||||
isMenuToggleable = [menuDict[OEGameCoreAdvancedMenuAllowsToggleKey] boolValue];
|
||||
isValidMenu = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidMenu)
|
||||
return;
|
||||
|
||||
// Handle option state changes
|
||||
for (NSMutableDictionary *menuDict in _advancedMenus)
|
||||
{
|
||||
if (menuDict[OEGameCoreAdvancedMenuGroupNameKey]){
|
||||
[self processAdvancedSubMenu:menuDict menuName:advancedMenuName menuID:menuID menuState:menuState menuPrefKey:menuPrefKey menuToggle:isMenuToggleable];
|
||||
}
|
||||
}
|
||||
|
||||
//send the menuname and menuID for actions to be taken
|
||||
[self advancedMenuAction:advancedMenuName menuID:menuID];
|
||||
}
|
||||
|
||||
- (void) processAdvancedSubMenu:(NSMutableDictionary *)parentMenuDict menuName:(NSString *)menuName menuID:(NSString *)menuID menuState:(BOOL)menuState menuPrefKey:(NSString *)menuPrefKey menuToggle:(BOOL)menuToggle;
|
||||
{
|
||||
for (NSMutableDictionary *subMenuDict in parentMenuDict[OEGameCoreAdvancedMenuGroupItemsKey])
|
||||
{
|
||||
//This function iterates through the menu items and sets the option and clears others in the same prefs group or toggles the menu item
|
||||
NSString *curMenuID = subMenuDict[OEGameCoreAdvancedMenuGroupIDKey];
|
||||
|
||||
if (subMenuDict[OEGameCoreAdvancedMenuGroupNameKey]){
|
||||
[self processAdvancedSubMenu:subMenuDict menuName:menuName menuID:menuID menuState:menuState menuPrefKey:menuPrefKey menuToggle:menuToggle];
|
||||
}
|
||||
else if ( [curMenuID isEqualToString:menuID] ){
|
||||
NSString *curMenuName = subMenuDict[OEGameCoreAdvancedMenuNameKey];
|
||||
NSString *curMenuKey = subMenuDict[OEGameCoreAdvancedMenuPrefKeyNameKey];
|
||||
|
||||
if (!curMenuName)
|
||||
continue;
|
||||
// Mutually exclusive option state change
|
||||
else if ([curMenuName isEqualToString:menuName] && !menuToggle)
|
||||
subMenuDict[OEGameCoreAdvancedMenuStateKey] = @YES;
|
||||
// Reset mutually exclusive options that are the same prefs group
|
||||
else if (!menuToggle && [curMenuKey isEqualToString:menuPrefKey])
|
||||
subMenuDict[OEGameCoreAdvancedMenuStateKey] = @NO;
|
||||
// Toggleable option state change
|
||||
else if ([curMenuName isEqualToString:menuName] && menuToggle)
|
||||
subMenuDict[OEGameCoreAdvancedMenuStateKey] = @(!menuState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *) findAdvancedSubMenu:(NSDictionary *)parentMenuDict menuName:(NSString *)menuName menuID:(NSString *)menuID
|
||||
{
|
||||
//This function iterates through the menu items and returns the dictionary item if found
|
||||
NSDictionary *testSubMenuDict;
|
||||
|
||||
for (NSDictionary *subMenuDict in parentMenuDict[OEGameCoreAdvancedMenuGroupItemsKey])
|
||||
{
|
||||
if (subMenuDict[OEGameCoreAdvancedMenuGroupNameKey])
|
||||
{
|
||||
testSubMenuDict = [self findAdvancedSubMenu:subMenuDict menuName:menuName menuID:menuID];
|
||||
|
||||
if (testSubMenuDict)
|
||||
{
|
||||
if ([testSubMenuDict[OEGameCoreAdvancedMenuNameKey] isEqualToString:menuName])
|
||||
{
|
||||
if ([testSubMenuDict[OEGameCoreAdvancedMenuGroupIDKey] isEqualToString:menuID])
|
||||
{
|
||||
return testSubMenuDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([subMenuDict[OEGameCoreAdvancedMenuNameKey] isEqualToString:menuName])
|
||||
{
|
||||
if ([subMenuDict[OEGameCoreAdvancedMenuGroupIDKey] isEqualToString:menuID])
|
||||
{
|
||||
return subMenuDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
# pragma mark - Advanced Menu Creation
|
||||
|
||||
- (NSArray <NSDictionary <NSString *, id> *> *)advancedMenu
|
||||
{
|
||||
//Create your menu array here. 4 levels deep at most
|
||||
if (_advancedMenus.count == 0)
|
||||
{
|
||||
_advancedMenus = [NSMutableArray array];
|
||||
|
||||
NSArray <NSDictionary <NSString *, id> *> *advancedMenuWithDefault =
|
||||
@[ // Level 1
|
||||
// @{ OEGameCoreAdvancedMenuGroupNameKey : @"Controllers",
|
||||
// OEGameCoreAdvancedMenuGroupIDKey : @"Controllers",
|
||||
// OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// // Level 2
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Controller 1",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller1",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 3
|
||||
OEAdvancedMenu_OptionDefault(@"Wiimote", @"Controller1", @"controller1"),
|
||||
OEAdvancedMenu_Option(@"Sideways Wiimote", @"Controller1", @"controller1"),
|
||||
OEAdvancedMenu_SeparatorItem(),
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Wiimote Attachment",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller1",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 4
|
||||
OEAdvancedMenu_OptionIndentedDefault(@"None", @"Controller1", @"attachment1"),
|
||||
OEAdvancedMenu_OptionIndented(@"Nunckuk", @"Controller1", @"attachment1"),
|
||||
OEAdvancedMenu_OptionIndented(@"Classic Controller", @"Controller1", @"attachment1"),
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
// Level 2
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Controller 2",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller2",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 3
|
||||
OEAdvancedMenu_OptionDefault(@"Wiimote", @"Controller2", @"controller2"),
|
||||
OEAdvancedMenu_Option(@"Sideways Wiimote", @"Controller2", @"controller2"),
|
||||
OEAdvancedMenu_SeparatorItem(),
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Wiimote Attachment",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller2",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 4
|
||||
OEAdvancedMenu_OptionIndentedDefault(@"None", @"Controller2", @"attachment2"),
|
||||
OEAdvancedMenu_OptionIndented(@"Nunckuk", @"Controller2", @"attachment2"),
|
||||
OEAdvancedMenu_OptionIndented(@"Classic Controller", @"Controller2", @"attachment2"),
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
// Level 2
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Controller 3",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller3",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 3
|
||||
OEAdvancedMenu_OptionDefault(@"Wiimote", @"Controller3", @"controller3"),
|
||||
OEAdvancedMenu_Option(@"Sideways Wiimote", @"Controller3", @"controller3"),
|
||||
OEAdvancedMenu_SeparatorItem(),
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Wiimote Attachment",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller3",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 4
|
||||
OEAdvancedMenu_OptionIndentedDefault(@"None", @"Controller3", @"attachment3"),
|
||||
OEAdvancedMenu_OptionIndented(@"Nunckuk", @"Controller3", @"attachment3"),
|
||||
OEAdvancedMenu_OptionIndented(@"Classic Controller", @"Controller3", @"attachment3"),
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
// Level 2
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Controller 4",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller4",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 3
|
||||
OEAdvancedMenu_OptionDefault(@"Wiimote", @"Controller4", @"controller4"),
|
||||
OEAdvancedMenu_Option(@"Sideways Wiimote", @"Controller4", @"controller4"),
|
||||
OEAdvancedMenu_SeparatorItem(),
|
||||
@{ OEGameCoreAdvancedMenuGroupNameKey : @"Wiimote Attachment",
|
||||
OEGameCoreAdvancedMenuGroupIDKey : @"Controller4",
|
||||
OEGameCoreAdvancedMenuGroupItemsKey : @[
|
||||
// Level 4
|
||||
OEAdvancedMenu_OptionIndentedDefault(@"None", @"Controller4", @"attachment4"),
|
||||
OEAdvancedMenu_OptionIndented(@"Nunckuk", @"Controller4", @"attachment4"),
|
||||
OEAdvancedMenu_OptionIndented(@"Classic Controller", @"Controller4", @"attachment4"),
|
||||
]
|
||||
},
|
||||
]
|
||||
// },
|
||||
// ]
|
||||
},
|
||||
];
|
||||
|
||||
// Deep mutable copy
|
||||
_advancedMenus = (NSMutableArray *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)advancedMenuWithDefault, kCFPropertyListMutableContainers));
|
||||
}
|
||||
return [_advancedMenus copy];
|
||||
}
|
||||
|
||||
# pragma mark - Advanced Menu Actions Processing
|
||||
|
||||
- (void)advancedMenuAction:(NSString *)advancedMenuName menuID:(NSString *)menuID
|
||||
{
|
||||
//Write your code to process Clicked menu items here
|
||||
int player = 0;
|
||||
|
||||
if ([menuID isEqualToString:@"Controller1"]){
|
||||
player = 1;
|
||||
}
|
||||
if ([menuID isEqualToString:@"Controller2"]){
|
||||
player = 2;
|
||||
}
|
||||
if ([menuID isEqualToString:@"Controller3"]){
|
||||
player = 3;
|
||||
}
|
||||
if ([menuID isEqualToString:@"Controller4"]){
|
||||
player = 4;
|
||||
}
|
||||
|
||||
if (player){
|
||||
if ([advancedMenuName isEqualToString:@"Wiimote"])
|
||||
{
|
||||
dol_host->setWiimoteSideways(player, false);
|
||||
return;
|
||||
}
|
||||
if ([advancedMenuName isEqualToString:@"Sideways Wiimote"])
|
||||
{
|
||||
dol_host->setWiimoteSideways(player, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([advancedMenuName isEqualToString:@"none"])
|
||||
{
|
||||
dol_host->changeWiimoteExtension(0, player);
|
||||
return;
|
||||
}
|
||||
if ([advancedMenuName isEqualToString:@"Nunchuk"])
|
||||
{
|
||||
dol_host->changeWiimoteExtension(1, player);
|
||||
return;
|
||||
}
|
||||
if ([advancedMenuName isEqualToString:@"Classic Controller"])
|
||||
{
|
||||
dol_host->changeWiimoteExtension(2, player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
+5
-3
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<string>5.0.13467</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>OEGameCoreController</string>
|
||||
<key>OEGameCoreClass</key>
|
||||
@@ -37,6 +37,8 @@
|
||||
<dict>
|
||||
<key>OEGameCoreHasGlitches</key>
|
||||
<true/>
|
||||
<key>OEGameCoreSupportsAdvancedMenu</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OEGameCorePlayerCount</key>
|
||||
@@ -49,7 +51,7 @@
|
||||
<string>openemu.system.wii</string>
|
||||
</array>
|
||||
<key>SUEnableAutomaticChecks</key>
|
||||
<true/>
|
||||
<string>1</string>
|
||||
<key>SUFeedURL</key>
|
||||
<string>https://raw.github.com/OpenEmu/OpenEmu-Update/master/dolphin_appcast.xml</string>
|
||||
</dict>
|
||||
|
||||
+1
-1
Submodule dolphin updated: a93b5a4a35...79a234eff7
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user