Add pre-launch settings, graphics API switching & more

This commit is contained in:
Luke Street 2022-06-12 17:09:47 -04:00
parent a3c2638cce
commit 97357146f5
11 changed files with 331 additions and 167 deletions

View File

@ -153,7 +153,6 @@ private:
CVarManager& m_cvarManager;
CVarCommons& m_cvarCommons;
ImGuiConsole m_imGuiConsole;
std::string m_errorString;
std::string m_deferredProject;
bool m_projectInitialized = false;
@ -166,7 +165,7 @@ private:
bool m_fullscreenToggleRequested = false;
bool m_quitRequested = false;
using delta_clock = std::chrono::high_resolution_clock;
std::chrono::time_point<delta_clock> m_prevFrameTime;
delta_clock::time_point m_prevFrameTime;
std::vector<u32> m_deferredControllers; // used to capture controllers added before CInputGenerator
// is built, i.e during initialization
@ -224,9 +223,9 @@ public:
m_projectInitialized = true;
} else {
Log.report(logvisor::Error, FMT_STRING("Failed to open disc image '{}'"), m_deferredProject);
m_errorString = fmt::format(FMT_STRING("Failed to open disc image '{}'"), m_deferredProject);
m_deferredProject.clear();
m_imGuiConsole.m_errorString = fmt::format(FMT_STRING("Failed to open disc image '{}'"), m_deferredProject);
}
m_deferredProject.clear();
}
const auto targetFrameTime = getTargetFrameTime();
@ -266,7 +265,14 @@ public:
if (!g_mainMP1 && m_projectInitialized) {
g_mainMP1.emplace(nullptr, nullptr);
g_mainMP1->Init(m_fileMgr, &m_cvarManager, m_voiceEngine.get(), *m_amuseAllocWrapper);
auto result = g_mainMP1->Init(m_fileMgr, &m_cvarManager, m_voiceEngine.get(), *m_amuseAllocWrapper);
if (!result.empty()) {
Log.report(logvisor::Error, FMT_STRING("{}"), result);
m_imGuiConsole.m_errorString = result;
g_mainMP1.reset();
CDvdFile::Shutdown();
m_projectInitialized = false;
}
}
float dt = 1 / 60.f;
@ -274,8 +280,8 @@ public:
dt = std::min(realDt, 1 / 30.f);
}
m_imGuiConsole.PreUpdate();
if (g_mainMP1) {
m_imGuiConsole.PreUpdate();
if (m_voiceEngine) {
m_voiceEngine->lockPump();
}
@ -285,15 +291,15 @@ public:
if (m_voiceEngine) {
m_voiceEngine->unlockPump();
}
m_imGuiConsole.PostUpdate();
} else {
auto result = m_imGuiConsole.ShowAboutWindow(false, m_errorString, true);
if (result) {
m_deferredProject = std::move(*result);
}
}
m_imGuiConsole.PostUpdate();
if (!g_mainMP1 && m_imGuiConsole.m_gameDiscSelected) {
std::optional<std::string> result;
m_imGuiConsole.m_gameDiscSelected.swap(result);
m_deferredProject = std::move(*result);
}
if (m_quitRequested) {
if (m_quitRequested || m_imGuiConsole.m_quitRequested || m_cvarManager.restartRequired()) {
if (g_mainMP1) {
g_mainMP1->Quit();
} else {
@ -489,7 +495,7 @@ public:
} // namespace metaforce
static void SetupBasics(bool logging) {
static void SetupBasics() {
#if _WIN32
if (logging && GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_UNKNOWN)
logvisor::CreateWin32Console();
@ -509,10 +515,6 @@ static void SetupBasics(bool logging) {
exit(1);
}
logvisor::RegisterStandardExceptions();
if (logging)
logvisor::RegisterConsoleLogger();
#if SENTRY_ENABLED
FileStoreManager fileMgr{"sentry-native-metaforce"};
std::string cacheDir{fileMgr.getStoreRoot()};
@ -531,7 +533,6 @@ static bool IsClientLoggingEnabled(int argc, char** argv) {
#if !WINDOWS_STORE
int main(int argc, char** argv) {
// TODO: This seems to fix a lot of weird issues with rounding
// but breaks animations, need to research why this is the case
// for now it's disabled
@ -541,36 +542,49 @@ int main(int argc, char** argv) {
return 100;
}
SetupBasics(IsClientLoggingEnabled(argc, argv));
SetupBasics();
metaforce::FileStoreManager fileMgr{"AxioDL", "metaforce"};
metaforce::CVarManager cvarMgr{fileMgr};
metaforce::CVarCommons cvarCmns{cvarMgr};
std::vector<std::string> args;
for (int i = 1; i < argc; ++i)
for (int i = 1; i < argc; ++i) {
args.emplace_back(argv[i]);
cvarMgr.parseCommandLine(args);
std::string logFile = cvarCmns.getLogFile();
std::string logFilePath;
if (!logFile.empty()) {
std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char buf[100];
std::strftime(buf, 100, "%Y-%m-%d_%H-%M-%S", std::localtime(&time));
logFilePath = fmt::format(FMT_STRING("{}/{}-{}"), fileMgr.getStoreRoot(), buf, logFile);
logvisor::RegisterFileLogger(logFilePath.c_str());
}
auto app = std::make_unique<metaforce::Application>(fileMgr, cvarMgr, cvarCmns);
auto icon = metaforce::GetIcon();
auto data = aurora::Icon{
.data = std::move(icon.data),
.width = icon.width,
.height = icon.height,
};
aurora::app_run(std::move(app), std::move(data), argc, argv, fileMgr.getStoreRoot(),
aurora::translate_backend(cvarCmns.getGraphicsApi()), cvarCmns.getSamples(),
cvarCmns.getAnisotropy());
bool restart = false;
do {
metaforce::CVarManager cvarMgr{fileMgr};
metaforce::CVarCommons cvarCmns{cvarMgr};
if (!restart) {
cvarMgr.parseCommandLine(args);
// TODO add clear loggers func to logvisor so we can recreate loggers on restart
logvisor::RegisterStandardExceptions();
if (IsClientLoggingEnabled(argc, argv)) {
logvisor::RegisterConsoleLogger();
}
std::string logFile = cvarCmns.getLogFile();
std::string logFilePath;
if (!logFile.empty()) {
std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char buf[100];
std::strftime(buf, 100, "%Y-%m-%d_%H-%M-%S", std::localtime(&time));
logFilePath = fmt::format(FMT_STRING("{}/{}-{}"), fileMgr.getStoreRoot(), buf, logFile);
logvisor::RegisterFileLogger(logFilePath.c_str());
}
}
auto app = std::make_unique<metaforce::Application>(fileMgr, cvarMgr, cvarCmns);
auto icon = metaforce::GetIcon();
auto data = aurora::Icon{
.data = std::move(icon.data),
.width = icon.width,
.height = icon.height,
};
aurora::app_run(std::move(app), std::move(data), argc, argv, fileMgr.getStoreRoot(),
aurora::backend_from_string(cvarCmns.getGraphicsApi()), cvarCmns.getSamples(),
cvarCmns.getAnisotropy(), cvarCmns.getFullscreen());
restart = cvarMgr.restartRequired();
} while (restart);
return 0;
}
#endif

View File

@ -37,8 +37,8 @@ enum class EGameplayResult { None, Win, Lose, Playing };
class IMain {
public:
virtual ~IMain() = default;
virtual void Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr,
boo::IAudioVoiceEngine* voiceEngine, amuse::IBackendVoiceAllocator& backend) = 0;
virtual std::string Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend) = 0;
virtual void Draw() = 0;
virtual bool Proc(float dt) = 0;
virtual void Shutdown() = 0;

View File

@ -41,6 +41,9 @@ std::array<ImGuiEntityEntry, kMaxEntities> ImGuiConsole::entities;
std::set<TUniqueId> ImGuiConsole::inspectingEntities;
ImGuiPlayerLoadouts ImGuiConsole::loadouts;
ImGuiConsole::ImGuiConsole(CVarManager& cvarMgr, CVarCommons& cvarCommons)
: m_cvarMgr(cvarMgr), m_cvarCommons(cvarCommons) {}
void ImGuiStringViewText(std::string_view text) {
// begin()/end() do not work on MSVC
ImGui::TextUnformatted(text.data(), text.data() + text.size());
@ -137,8 +140,10 @@ static void Warp(const CAssetId worldId, TAreaId aId) {
}
void ImGuiConsole::ShowMenuGame() {
m_paused = g_Main->IsPaused();
if (ImGui::MenuItem("Paused", "F5", &m_paused)) {
if (g_Main != nullptr) {
m_paused = g_Main->IsPaused();
}
if (ImGui::MenuItem("Paused", "F5", &m_paused, g_Main != nullptr)) {
g_Main->SetPaused(m_paused);
}
if (ImGui::MenuItem("Step Frame", "F6", &m_stepFrame, m_paused)) {
@ -159,7 +164,7 @@ void ImGuiConsole::ShowMenuGame() {
ImGui::EndMenu();
}
if (ImGui::MenuItem("Quit", "Alt+F4")) {
g_Main->Quit();
m_quitRequested = true;
}
}
@ -640,12 +645,10 @@ void ImGuiConsole::ShowConsoleVariablesWindow() {
ImGui::End();
}
std::optional<std::string> ImGuiConsole::ShowAboutWindow(bool canClose, std::string_view errorString, bool preLaunch) {
std::optional<std::string> result{};
void ImGuiConsole::ShowAboutWindow(bool preLaunch) {
// Center window
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, canClose ? ImGuiCond_Appearing : ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowPos(center, preLaunch ? ImGuiCond_Always : ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImVec4& windowBg = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
ImGui::PushStyleColor(ImGuiCol_TitleBg, windowBg);
@ -654,10 +657,10 @@ std::optional<std::string> ImGuiConsole::ShowAboutWindow(bool canClose, std::str
bool* open = nullptr;
ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNav |
ImGuiWindowFlags_NoSavedSettings;
if (canClose) {
open = &m_showAboutWindow;
} else {
if (preLaunch) {
flags |= ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove;
} else {
open = &m_showAboutWindow;
}
if (ImGui::Begin("About", open, flags)) {
float iconSize = 128.f * aurora::get_window_size().scale;
@ -670,12 +673,16 @@ std::optional<std::string> ImGuiConsole::ShowAboutWindow(bool canClose, std::str
const ImVec2& padding = ImGui::GetStyle().WindowPadding;
ImGui::Dummy(padding);
if (preLaunch) {
if (ImGuiButtonCenter("Settings")) {
m_showPreLaunchSettingsWindow = true;
}
ImGui::Dummy(padding);
#ifdef NATIVEFILEDIALOG_SUPPORTED
if (ImGuiButtonCenter("Select Game Disc")) {
nfdchar_t* outPath = nullptr;
nfdresult_t nfdResult = NFD_OpenDialog(nullptr, nullptr, &outPath);
if (nfdResult == NFD_OKAY) {
result = outPath;
m_gameDiscSelected = outPath;
free(outPath);
} else if (nfdResult != NFD_CANCEL) {
Log.report(logvisor::Error, FMT_STRING("nativefiledialog error: {}"), NFD_GetError());
@ -684,9 +691,9 @@ std::optional<std::string> ImGuiConsole::ShowAboutWindow(bool canClose, std::str
ImGui::Dummy(padding);
#endif
}
if (!errorString.empty()) {
if (m_errorString) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.77f, 0.12f, 0.23f, 1.f});
ImGuiTextCenter(errorString);
ImGuiTextCenter(*m_errorString);
ImGui::PopStyleColor();
ImGui::Dummy(padding);
}
@ -764,7 +771,6 @@ std::optional<std::string> ImGuiConsole::ShowAboutWindow(bool canClose, std::str
}
ImGui::End();
ImGui::PopStyleColor(2);
return result;
}
static std::string BytesToString(size_t bytes) {
@ -932,7 +938,7 @@ void ImGuiConsole::ShowDebugOverlay() {
ImGuiStringViewText(
fmt::format(FMT_STRING("CRandom16::Next calls: {}\n"), metaforce::CRandom16::GetNumNextCalls()));
}
if (m_resourceStats) {
if (m_resourceStats && g_SimplePool != nullptr) {
if (hasPrevious) {
ImGui::Separator();
}
@ -1168,7 +1174,7 @@ void ImGuiConsole::SetOverlayWindowLocation(int corner) const {
ImGui::SetNextWindowPos(windowPos, ImGuiCond_Always, windowPosPivot);
}
void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) {
void ImGuiConsole::ShowAppMainMenuBar(bool canInspect, bool preLaunch) {
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("Game")) {
ShowMenuGame();
@ -1221,7 +1227,7 @@ void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) {
}
ImGui::Spacing();
if (ImGui::BeginMenu("Help")) {
ImGui::MenuItem("About", nullptr, &m_showAboutWindow);
ImGui::MenuItem("About", nullptr, &m_showAboutWindow, !preLaunch);
ImGui::Separator();
if (ImGui::BeginMenu("ImGui")) {
if (ImGui::MenuItem("Clear Settings")) {
@ -1240,6 +1246,7 @@ void ImGuiConsole::ShowAppMainMenuBar(bool canInspect) {
void ImGuiConsole::PreUpdate() {
OPTICK_EVENT();
bool preLaunch = g_Main == nullptr;
if (!m_isInitialized) {
m_isInitialized = true;
m_cvarCommons.m_debugOverlayShowFrameCounter->addListener([this](CVar* c) { m_frameCounter = c->toBoolean(); });
@ -1255,33 +1262,38 @@ void ImGuiConsole::PreUpdate() {
m_cvarCommons.m_debugOverlayShowInput->addListener([this](CVar* c) { m_showInput = c->toBoolean(); });
m_cvarMgr.findCVar("developer")->addListener([this](CVar* c) { m_developer = c->toBoolean(); });
m_cvarMgr.findCVar("cheats")->addListener([this](CVar* c) { m_cheats = c->toBoolean(); });
}
if (!preLaunch && !m_isLaunchInitialized) {
if (m_developer) {
m_toasts.emplace_back("Press ` to toggle menu"s, 5.f);
}
m_isLaunchInitialized = true;
}
// We ned to make sure we have a valid CRandom16 at all times, so lets do that here
// We need to make sure we have a valid CRandom16 at all times, so let's do that here
if (g_StateManager != nullptr && g_StateManager->GetActiveRandom() == nullptr) {
g_StateManager->SetActiveRandomToDefault();
}
if (ImGui::IsKeyReleased(ImGuiKey_GraveAccent)) {
m_isVisible ^= 1;
}
if (m_stepFrame) {
g_Main->SetPaused(true);
m_stepFrame = false;
}
if (m_paused && !m_stepFrame && ImGui::IsKeyPressed(ImGuiKey_F6)) {
g_Main->SetPaused(false);
m_stepFrame = true;
}
if (ImGui::IsKeyReleased(ImGuiKey_F5)) {
m_paused ^= 1;
g_Main->SetPaused(m_paused);
if (!preLaunch) {
if (ImGui::IsKeyReleased(ImGuiKey_GraveAccent)) {
m_isVisible ^= 1;
}
if (m_stepFrame) {
g_Main->SetPaused(true);
m_stepFrame = false;
}
if (m_paused && !m_stepFrame && ImGui::IsKeyPressed(ImGuiKey_F6)) {
g_Main->SetPaused(false);
m_stepFrame = true;
}
if (ImGui::IsKeyReleased(ImGuiKey_F5)) {
m_paused ^= 1;
g_Main->SetPaused(m_paused);
}
}
bool canInspect = g_StateManager != nullptr && g_StateManager->GetObjectList();
if (m_isVisible) {
ShowAppMainMenuBar(canInspect);
if (preLaunch || m_isVisible) {
ShowAppMainMenuBar(canInspect, preLaunch);
}
ShowToasts();
if (canInspect && (m_showInspectWindow || !inspectingEntities.empty())) {
@ -1304,8 +1316,8 @@ void ImGuiConsole::PreUpdate() {
if (canInspect && m_showLayersWindow) {
ShowLayersWindow();
}
if (m_showAboutWindow) {
ShowAboutWindow(true);
if (preLaunch || m_showAboutWindow) {
ShowAboutWindow(preLaunch);
}
if (m_showDemoWindow) {
ImGui::ShowDemoWindow(&m_showDemoWindow);
@ -1318,6 +1330,9 @@ void ImGuiConsole::PreUpdate() {
ShowPlayerTransformEditor();
ShowPipelineProgress();
m_controllerConfig.show(m_controllerConfigVisible);
if (preLaunch && m_showPreLaunchSettingsWindow) {
ShowPreLaunchSettingsWindow();
}
}
void ImGuiConsole::PostUpdate() {
@ -1345,7 +1360,7 @@ void ImGuiConsole::PostUpdate() {
// Always calculate room time regardless of if the overlay is displayed, this allows us have an accurate display if
// the user chooses to display it later on during gameplay
if (g_StateManager && m_currentRoom != g_StateManager->GetCurrentArea()) {
if (g_StateManager != nullptr && m_currentRoom != g_StateManager->GetCurrentArea()) {
const double igt = g_GameState->GetTotalPlayTime();
m_currentRoom = static_cast<const void*>(g_StateManager->GetCurrentArea());
m_lastRoomTime = igt - m_currentRoomStart;
@ -1781,4 +1796,66 @@ void ImGuiConsole::ControllerAdded(uint32_t idx) {
void ImGuiConsole::ControllerRemoved(uint32_t idx) {
m_toasts.emplace_back(fmt::format(FMT_STRING("Controller {} disconnected"), idx), 5.f);
}
static void ImGuiCVarCheckbox(CVarManager& mgr, std::string_view cvarName, const char* label, bool* ptr = nullptr) {
auto* cvar = mgr.findOrMakeCVar(cvarName, ""sv, false, CVar::EFlags::Game | CVar::EFlags::Archive);
if (cvar != nullptr) {
bool value = cvar->toBoolean();
bool modified = false;
if (ptr == nullptr) {
modified = ImGui::Checkbox(label, &value);
} else {
modified = ImGui::Checkbox(label, ptr);
value = *ptr;
}
if (modified) {
cvar->unlock();
cvar->fromBoolean(value);
cvar->lock();
}
}
}
void ImGuiConsole::ShowPreLaunchSettingsWindow() {
if (ImGui::Begin("Settings", &m_showPreLaunchSettingsWindow, ImGuiWindowFlags_AlwaysAutoResize)) {
if (ImGui::BeginTabBar("Settings")) {
if (ImGui::BeginTabItem("Graphics")) {
static auto AvailableBackends = aurora::get_available_backends();
ImGuiStringViewText(fmt::format(FMT_STRING("Current backend: {}"), aurora::get_backend_string()));
auto desiredBackend = static_cast<int>(aurora::Backend::Invalid);
if (auto* cvar = m_cvarMgr.findCVar("graphicsApi")) {
bool valid = false;
const auto name = cvar->toLiteral(&valid);
if (valid) {
desiredBackend = static_cast<int>(aurora::backend_from_string(name));
}
}
bool modified = false;
modified = ImGui::RadioButton("Auto", &desiredBackend, static_cast<int>(aurora::Backend::Invalid));
for (const auto& item : AvailableBackends) {
modified = ImGui::RadioButton(magic_enum::enum_name(item).data(), &desiredBackend, static_cast<int>(item)) ||
modified;
}
if (modified) {
m_cvarCommons.m_graphicsApi->fromLiteral(
aurora::backend_to_string(static_cast<aurora::Backend>(desiredBackend)));
}
ImGuiCVarCheckbox(m_cvarMgr, "fullscreen", "Fullscreen");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Game")) {
ImGuiCVarCheckbox(m_cvarMgr, "tweak.game.SplashScreensDisabled", "Skip Splash Screens");
ImGuiCVarCheckbox(m_cvarMgr, "developer", "Developer Mode", &m_developer);
ImGuiCVarCheckbox(m_cvarMgr, "cheats", "Enable Cheats", &m_cheats);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Experimental")) {
ImGuiCVarCheckbox(m_cvarMgr, "variableDt", "Variable Delta Time (broken)");
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
ImGui::End();
}
} // namespace metaforce

View File

@ -51,12 +51,10 @@ public:
static std::array<ImGuiEntityEntry, kMaxEntities> entities;
static ImGuiPlayerLoadouts loadouts;
ImGuiConsole(CVarManager& cvarMgr, CVarCommons& cvarCommons) : m_cvarMgr(cvarMgr), m_cvarCommons(cvarCommons) {}
ImGuiConsole(CVarManager& cvarMgr, CVarCommons& cvarCommons);
void PreUpdate();
void PostUpdate();
void Shutdown();
std::optional<std::string> ShowAboutWindow(bool canClose, std::string_view errorString = ""sv,
bool preLaunch = false);
static void BeginEntityRow(const ImGuiEntityEntry& entry);
static void EndEntityRow(const ImGuiEntityEntry& entry);
@ -64,6 +62,10 @@ public:
void ControllerAdded(uint32_t idx);
void ControllerRemoved(uint32_t idx);
std::optional<std::string> m_errorString;
std::optional<std::string> m_gameDiscSelected;
bool m_quitRequested = false;
private:
CVarManager& m_cvarMgr;
CVarCommons& m_cvarCommons;
@ -75,6 +77,7 @@ private:
bool m_showLayersWindow = false;
bool m_showConsoleVariablesWindow = false;
bool m_showPlayerTransformEditor = false;
bool m_showPreLaunchSettingsWindow = false;
std::optional<zeus::CVector3f> m_savedLocation;
std::optional<zeus::CEulerAngles> m_savedRotation;
@ -113,6 +116,7 @@ private:
bool m_developer = m_cvarMgr.findCVar("developer")->toBoolean();
bool m_cheats = m_cvarMgr.findCVar("cheats")->toBoolean();
bool m_isInitialized = false;
bool m_isLaunchInitialized = false;
int m_debugOverlayCorner = 2; // bottom-left
int m_inputOverlayCorner = 3; // bottom-right
@ -126,7 +130,8 @@ private:
bool m_controllerConfigVisible = false;
ImGuiControllerConfig m_controllerConfig;
void ShowAppMainMenuBar(bool canInspect);
void ShowAboutWindow(bool preLaunch);
void ShowAppMainMenuBar(bool canInspect, bool preLaunch);
void ShowMenuGame();
bool ShowEntityInfoWindow(TUniqueId uid);
void ShowInspectWindow(bool* isOpen);
@ -142,5 +147,6 @@ private:
void ShowCornerContextMenu(int& corner, int avoidCorner) const;
void ShowPlayerTransformEditor();
void ShowPipelineProgress();
void ShowPreLaunchSettingsWindow();
};
} // namespace metaforce

View File

@ -240,6 +240,8 @@ CMain::CMain(IFactory* resFactory, CSimplePool* resStore)
g_Main = this;
}
CMain::~CMain() { g_Main = nullptr; }
void CMain::RegisterResourceTweaks() {}
void CGameGlobalObjects::AddPaksAndFactories() {
@ -516,15 +518,14 @@ void CMain::HandleDiscordErrored(int errorCode, const char* message) {
DiscordLog.report(logvisor::Error, FMT_STRING("Discord Error: {}"), message);
}
void CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend) {
InitializeDiscord();
std::string CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend) {
m_cvarMgr = cvarMgr;
{
const auto discInfo = CDvdFile::DiscInfo();
auto discInfo = CDvdFile::DiscInfo();
if (discInfo.gameId[4] != '0' || discInfo.gameId[5] != '1') {
Log.report(logvisor::Fatal, FMT_STRING("Unknown game ID {}"), std::string_view{discInfo.gameId.data(), 6});
return fmt::format(FMT_STRING("Unknown game ID {}"), std::string_view{discInfo.gameId.data(), 6});
}
if (strncmp(discInfo.gameId.data(), "GM8", 3) == 0) {
m_version.game = EGame::MetroidPrime1;
@ -545,7 +546,7 @@ void CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IA
m_version.game = EGame::MetroidPrimeTrilogy;
m_version.platform = EPlatform::Wii;
} else {
Log.report(logvisor::Fatal, FMT_STRING("Unknown game ID {}"), std::string_view{discInfo.gameId.data(), 6});
return fmt::format(FMT_STRING("Unknown game ID {}"), std::string_view{discInfo.gameId.data(), 6});
}
switch (discInfo.gameId[3]) {
case 'E':
@ -562,13 +563,13 @@ void CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IA
m_version.region = ERegion::PAL;
break;
default:
Log.report(logvisor::Fatal, FMT_STRING("Unknown region {}"), discInfo.gameId[3]);
return fmt::format(FMT_STRING("Unknown region {}"), discInfo.gameId[3]);
}
m_version.gameTitle = std::move(discInfo.gameTitle);
}
if (m_version.game != EGame::MetroidPrime1 && m_version.game != EGame::MetroidPrimeTrilogy) {
Log.report(logvisor::Fatal, FMT_STRING("Unsupported game {}"), magic_enum::enum_name(m_version.game));
return fmt::format(FMT_STRING("Unsupported game {}"), magic_enum::enum_name(m_version.game));
}
{
@ -580,19 +581,20 @@ void CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IA
}
CDvdFile file(dolFile);
if (!file) {
Log.report(logvisor::Fatal, FMT_STRING("Failed to open {}"), dolFile);
return fmt::format(FMT_STRING("Failed to open {}"), dolFile);
}
std::unique_ptr<u8[]> buf = std::make_unique<u8[]>(file.Length());
u32 readLen = file.SyncRead(buf.get(), file.Length());
const char* buildInfo = static_cast<char*>(memmem(buf.get(), readLen, "MetroidBuildInfo", 16)) + 19;
if (buildInfo == nullptr) {
Log.report(logvisor::Fatal, FMT_STRING("Failed to locate MetroidBuildInfo"));
return fmt::format(FMT_STRING("Failed to locate MetroidBuildInfo"));
}
m_version.version = buildInfo;
}
MainLog.report(logvisor::Level::Info, FMT_STRING("Loading data from {} {} ({})"), GetGameTitle(),
magic_enum::enum_name(GetRegion()), GetVersionString());
InitializeDiscord();
if (m_version.game == EGame::MetroidPrimeTrilogy) {
CDvdFile::SetRootDirectory("MP1");
} else if (m_version.platform == EPlatform::Wii) {
@ -657,6 +659,7 @@ void CMain::Init(const FileStoreManager& storeMgr, CVarManager* cvarMgr, boo::IA
x164_archSupport->PreloadAudio();
std::srand(static_cast<u32>(CBasics::GetTime()));
// g_TweakManager->ReadFromMemoryCard("AudioTweaks");
return {};
}
bool CMain::Proc(float dt) {

View File

@ -236,6 +236,7 @@ private:
public:
CMain(IFactory* resFactory, CSimplePool* resStore);
~CMain();
void RegisterResourceTweaks();
void AddWorldPaks();
void AddOverridePaks();
@ -251,8 +252,8 @@ public:
// int RsMain(int argc, char** argv, boo::IAudioVoiceEngine* voiceEngine, amuse::IBackendVoiceAllocator&
// backend);
void Init(const FileStoreManager& storeMgr, CVarManager* cvarManager, boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend) override;
std::string Init(const FileStoreManager& storeMgr, CVarManager* cvarManager, boo::IAudioVoiceEngine* voiceEngine,
amuse::IBackendVoiceAllocator& backend) override;
bool Proc(float dt) override;
void Draw() override;
void Shutdown() override;

View File

@ -243,14 +243,18 @@ struct AppDelegate {
};
void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv, std::string_view configPath,
Backend desiredBackend = Backend::Invalid, uint32_t msaa = 1, uint16_t aniso = 16) noexcept;
Backend desiredBackend = Backend::Invalid, uint32_t msaa = 1, uint16_t aniso = 16,
bool fullscreen = false) noexcept;
[[nodiscard]] std::vector<std::string> get_args() noexcept;
[[nodiscard]] WindowSize get_window_size() noexcept;
void set_window_title(zstring_view title) noexcept;
[[nodiscard]] Backend get_backend() noexcept;
[[nodiscard]] std::vector<Backend> get_available_backends() noexcept;
[[nodiscard]] std::string_view get_backend_string() noexcept;
[[nodiscard]] Backend translate_backend(std::string_view name);
[[nodiscard]] Backend backend_from_string(std::string_view name);
[[nodiscard]] std::string_view backend_to_string(Backend backend);
void set_fullscreen(bool fullscreen) noexcept;
bool is_fullscreen() noexcept;
[[nodiscard]] uint32_t get_which_controller_for_player(int32_t index) noexcept;
[[nodiscard]] int32_t get_controller_player_index(uint32_t which) noexcept;
void set_controller_player_index(uint32_t which, int32_t index) noexcept;

View File

@ -48,6 +48,24 @@ static void set_window_icon(Icon icon) noexcept {
SDL_FreeSurface(iconSurface);
}
static bool g_paused = false;
static void resize_swapchain(bool force) noexcept {
const auto size = get_window_size();
if (!force && size == g_windowSize) {
return;
}
if (size.scale != g_windowSize.scale) {
if (g_windowSize.scale > 0.f) {
Log.report(logvisor::Info, FMT_STRING("Display scale changed to {}"), size.scale);
}
g_AppDelegate->onAppDisplayScaleChanged(size.scale);
}
g_windowSize = size;
gpu::resize_swapchain(size.fb_width, size.fb_height);
g_AppDelegate->onAppWindowResized(size);
}
static bool poll_events() noexcept {
SDL_Event event;
while (SDL_PollEvent(&event) != 0) {
@ -56,69 +74,22 @@ static bool poll_events() noexcept {
switch (event.type) {
case SDL_WINDOWEVENT: {
switch (event.window.event) {
case SDL_WINDOWEVENT_SHOWN: {
case SDL_WINDOWEVENT_MINIMIZED: {
// Android/iOS: Application backgrounded
g_paused = true;
break;
}
case SDL_WINDOWEVENT_HIDDEN: {
case SDL_WINDOWEVENT_RESTORED: {
// Android/iOS: Application focused
g_paused = false;
break;
}
case SDL_WINDOWEVENT_MOVED: {
g_AppDelegate->onAppWindowMoved(event.window.data1, event.window.data2);
break;
}
case SDL_WINDOWEVENT_EXPOSED:
#if SDL_VERSION_ATLEAST(2, 0, 18)
case SDL_WINDOWEVENT_DISPLAY_CHANGED:
#endif
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_MINIMIZED:
case SDL_WINDOWEVENT_MAXIMIZED: {
const auto size = get_window_size();
if (size == g_windowSize) {
break;
}
if (size.scale != g_windowSize.scale) {
if (g_windowSize.scale > 0.f) {
Log.report(logvisor::Info, FMT_STRING("Display scale changed to {}"), size.scale);
}
g_AppDelegate->onAppDisplayScaleChanged(size.scale);
}
g_windowSize = size;
gpu::resize_swapchain(size.fb_width, size.fb_height);
g_AppDelegate->onAppWindowResized(size);
break;
}
case SDL_WINDOWEVENT_RESTORED: {
// TODO: handle restored event
break;
}
case SDL_WINDOWEVENT_ENTER: {
// TODO: handle enter event (mouse focus)
break;
}
case SDL_WINDOWEVENT_LEAVE: {
// TODO: handle leave event (mouse focus)
break;
}
case SDL_WINDOWEVENT_FOCUS_GAINED: {
// TODO: handle focus gained event
break;
}
case SDL_WINDOWEVENT_FOCUS_LOST: {
// TODO: handle focus lost event
break;
}
case SDL_WINDOWEVENT_CLOSE: {
// TODO: handle window close event
break;
}
case SDL_WINDOWEVENT_TAKE_FOCUS: {
// TODO: handle take focus event
break;
}
case SDL_WINDOWEVENT_HIT_TEST: {
// TODO: handle hit test?
case SDL_WINDOWEVENT_SIZE_CHANGED: {
resize_swapchain(false);
break;
}
}
@ -156,6 +127,12 @@ static bool poll_events() noexcept {
break;
}
case SDL_KEYDOWN: {
// ALT+ENTER for fullscreen toggle
if (event.key.repeat == 0u && event.key.keysym.scancode == SDL_SCANCODE_RETURN &&
(event.key.keysym.mod & (KMOD_RALT | KMOD_LALT)) != 0u) {
set_fullscreen(!is_fullscreen());
break;
}
if (!ImGui::GetIO().WantCaptureKeyboard) {
SpecialKey specialKey{};
ModifierKey modifierKey{};
@ -226,12 +203,15 @@ static bool poll_events() noexcept {
return true;
}
static SDL_Window* create_window(wgpu::BackendType type) {
static SDL_Window* create_window(wgpu::BackendType type, bool fullscreen) {
Uint32 flags = SDL_WINDOW_ALLOW_HIGHDPI;
#if TARGET_OS_IOS || TARGET_OS_TV || defined(__SWITCH__)
#if TARGET_OS_IOS || TARGET_OS_TV
flags |= SDL_WINDOW_FULLSCREEN;
#else
flags |= SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
if (fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN;
}
#endif
switch (type) {
#ifdef DAWN_ENABLE_BACKEND_VULKAN
@ -252,7 +232,13 @@ static SDL_Window* create_window(wgpu::BackendType type) {
default:
break;
}
return SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 960, flags);
return SDL_CreateWindow("Metaforce", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280,
#ifdef __SWITCH__
720,
#else
960,
#endif
flags);
}
wgpu::BackendType to_wgpu_backend(Backend backend) {
@ -272,12 +258,11 @@ wgpu::BackendType to_wgpu_backend(Backend backend) {
case Backend::OpenGLES:
return wgpu::BackendType::OpenGLES;
default:
case Backend::Null:
return wgpu::BackendType::Null;
}
}
void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv, std::string_view configPath,
Backend desiredBackend, uint32_t msaa, uint16_t aniso) noexcept {
Backend desiredBackend, uint32_t msaa, uint16_t aniso, bool fullscreen) noexcept {
g_AppDelegate = std::move(app);
/* Lets gather arguments skipping the program filename */
for (size_t i = 1; i < argc; ++i) {
@ -307,7 +292,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
/* Attempt to create a window using the calling application's desired backend */
if (desiredBackend != Backend::Invalid) {
auto wgpuBackend = to_wgpu_backend(desiredBackend);
g_window = create_window(wgpuBackend);
g_window = create_window(wgpuBackend, fullscreen);
if (g_window != nullptr && !gpu::initialize(g_window, wgpuBackend, msaa, aniso)) {
g_window = nullptr;
SDL_DestroyWindow(g_window);
@ -316,7 +301,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
if (g_window == nullptr) {
for (const auto backendType : gpu::PreferredBackendOrder) {
auto* window = create_window(backendType);
auto* window = create_window(backendType, fullscreen);
if (window == nullptr) {
continue;
}
@ -365,12 +350,22 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
g_AppDelegate->onAppWindowResized(size);
while (poll_events()) {
if (g_paused) {
continue;
}
imgui::new_frame(g_windowSize);
if (!g_AppDelegate->onAppIdle(ImGui::GetIO().DeltaTime)) {
break;
}
const wgpu::TextureView view = g_swapChain.GetCurrentTextureView();
if (!view) {
ImGui::EndFrame();
// Force swapchain recreation
resize_swapchain(true);
continue;
}
gfx::begin_frame();
g_AppDelegate->onAppDraw();
@ -421,6 +416,7 @@ void app_run(std::unique_ptr<AppDelegate> app, Icon icon, int argc, char** argv,
SDL_DestroyRenderer(renderer);
}
SDL_DestroyWindow(g_window);
g_window = nullptr;
SDL_EnableScreenSaver();
SDL_Quit();
}
@ -468,14 +464,39 @@ Backend get_backend() noexcept {
return Backend::OpenGL;
case wgpu::BackendType::OpenGLES:
return Backend::OpenGLES;
case wgpu::BackendType::Null:
return Backend::Null;
default:
return Backend::Invalid;
}
}
std::vector<Backend> get_available_backends() noexcept {
return {
#ifdef DAWN_ENABLE_BACKEND_D3D12
Backend::D3D12,
#endif
#ifdef DAWN_ENABLE_BACKEND_METAL
Backend::Metal,
#endif
#ifdef DAWN_ENABLE_BACKEND_VULKAN
Backend::Vulkan,
#endif
#ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
Backend::OpenGL,
#endif
#ifdef DAWN_ENABLE_BACKEND_OPENGLES
Backend::OpenGLES,
#endif
#ifdef DAWN_ENABLE_BACKEND_NULL
Backend::Null,
#endif
};
}
std::string_view get_backend_string() noexcept { return magic_enum::enum_name(gpu::g_backendType); }
Backend translate_backend(std::string_view backend) {
Backend backend_from_string(std::string_view backend) {
if (backend.empty()) {
return Backend::Invalid;
}
@ -524,10 +545,35 @@ Backend translate_backend(std::string_view backend) {
return Backend::Invalid;
}
std::string_view backend_to_string(Backend backend) {
switch (backend) {
case Backend::Null:
return "null"sv;
case Backend::WebGPU:
return "webgpu"sv;
case Backend::D3D11:
return "d3d11"sv;
case Backend::D3D12:
return "d3d12"sv;
case Backend::Metal:
return "metal"sv;
case Backend::Vulkan:
return "vulkan"sv;
case Backend::OpenGL:
return "opengl"sv;
case Backend::OpenGLES:
return "opengles"sv;
case Backend::Invalid:
return "auto";
}
}
void set_fullscreen(bool fullscreen) noexcept {
SDL_SetWindowFullscreen(g_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
}
bool is_fullscreen() noexcept { return (SDL_GetWindowFlags(g_window) & SDL_WINDOW_FULLSCREEN) != 0u; }
uint32_t get_which_controller_for_player(int32_t index) noexcept { return input::get_instance_for_player(index); }
int32_t get_controller_player_index(uint32_t instance) noexcept { return input::player_index(instance); }

View File

@ -313,7 +313,10 @@ static void pipeline_worker() {
void initialize() {
// No async pipelines for OpenGL (ES)
if (gpu::g_backendType != wgpu::BackendType::OpenGL && gpu::g_backendType != wgpu::BackendType::OpenGLES) {
if (gpu::g_backendType == wgpu::BackendType::OpenGL || gpu::g_backendType == wgpu::BackendType::OpenGLES) {
g_hasPipelineThread = false;
} else {
g_pipelineThreadEnd = false;
g_pipelineThread = std::thread(pipeline_worker);
g_hasPipelineThread = true;
}
@ -425,21 +428,30 @@ void shutdown() {
file.write(reinterpret_cast<const char*>(&g_serializedPipelineCount), sizeof(g_serializedPipelineCount));
file.write(reinterpret_cast<const char*>(g_serializedPipelines.data()), g_serializedPipelines.size());
}
g_serializedPipelines.clear();
g_serializedPipelineCount = 0;
}
gx::shutdown();
g_resolvedTextures.clear();
g_textureUploads.clear();
g_cachedBindGroups.clear();
g_cachedSamplers.clear();
g_pipelines.clear();
g_queuedPipelines.clear();
g_vertexBuffer = {};
g_uniformBuffer = {};
g_indexBuffer = {};
g_storageBuffer = {};
g_stagingBuffers.fill({});
g_renderPasses.clear();
g_currentRenderPass = 0;
g_state = {};
queuedPipelines = 0;
createdPipelines = 0;
}
static size_t currentStagingBuffer = 0;

View File

@ -245,9 +245,10 @@ bool initialize(SDL_Window* window, wgpu::BackendType backendType, uint32_t msaa
Log.report(logvisor::Info, FMT_STRING("Creating Dawn instance"));
g_Instance = std::make_unique<dawn::native::Instance>();
}
#ifndef NDEBUG
Log.report(logvisor::Info, FMT_STRING("Attempting to initialize {}"), magic_enum::enum_name(backendType));
#if 0
// D3D12's debug layer is very slow
// g_Instance->EnableBackendValidation(backendType != wgpu::BackendType::D3D12);
g_Instance->EnableBackendValidation(backendType != wgpu::BackendType::D3D12);
#endif
if (!utils::DiscoverAdapter(g_Instance.get(), window, backendType)) {
return false;

2
extern/dawn vendored

@ -1 +1 @@
Subproject commit 369c345a2d40e46b926a1ec5ae75720f2c188247
Subproject commit 8341499b8627f9123b14158dbeeeadef9c9e7f4d