#include "gui/GameProfileWindow.h" #include #include #include #include #include #include #include #include "gui/helpers/wxHelpers.h" #include "input/InputManager.h" #if BOOST_OS_LINUX || BOOST_OS_MACOS #include "resource/embedded/resources.h" #endif GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) : wxFrame(parent, wxID_ANY, _("Edit game profile"), wxDefaultPosition, wxSize{ 390, 350 }, wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER | wxTAB_TRAVERSAL | wxSYSTEM_MENU), m_title_id(title_id) { SetIcon(wxICON(X_GAME_PROFILE)); m_game_profile.Reset(); m_game_profile.Load(title_id); this->SetSizeHints(wxDefaultSize, wxDefaultSize); auto* main_sizer = new wxBoxSizer(wxVERTICAL); auto* m_notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); // general { auto* panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); auto* sizer = new wxBoxSizer(wxVERTICAL); { auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, _("General")), wxVERTICAL); auto* box = box_sizer->GetStaticBox(); m_load_libs = new wxCheckBox(box, wxID_ANY, _("Load shared libraries")); m_load_libs->SetToolTip(_("EXPERT OPTION\nThis option will load libraries from the cafeLibs directory")); box_sizer->Add(m_load_libs, 0, wxALL, 5); m_start_with_padview = new wxCheckBox(box, wxID_ANY, _("Launch with gamepad view")); m_start_with_padview->SetToolTip(_("Games will be launched with gamepad view toggled as default. The view can be toggled with CTRL + TAB")); box_sizer->Add(m_start_with_padview, 0, wxALL, 5); sizer->Add(box_sizer, 0, wxEXPAND, 5); } // cpu { auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, _("CPU")), wxVERTICAL); auto* box = box_sizer->GetStaticBox(); auto* first_row = new wxFlexGridSizer(0, 2, 0, 0); first_row->SetFlexibleDirection(wxBOTH); first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); first_row->Add(new wxStaticText(box, wxID_ANY, _("Mode")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString cpu_modes[] = { _("Single-core interpreter"), _("Single-core recompiler"), _("Multi-core recompiler"), _("Auto (recommended)") }; const sint32 m_cpu_modeNChoices = std::size(cpu_modes); m_cpu_mode = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cpu_modeNChoices, cpu_modes, 0); m_cpu_mode->SetToolTip(_("Set the CPU emulation mode")); first_row->Add(m_cpu_mode, 0, wxALL, 5); first_row->Add(new wxStaticText(box, wxID_ANY, _("Thread quantum")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); auto* quantum_sizer = new wxFlexGridSizer(0, 2, 0, 0); quantum_sizer->SetFlexibleDirection(wxBOTH); quantum_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); wxString quantum_values[] = { "20000", "45000", "60000", "80000" ,"100000" }; m_thread_quantum = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(quantum_values), quantum_values); m_thread_quantum->SetMinSize(wxSize(85, -1)); m_thread_quantum->SetToolTip(_("EXPERT OPTION\nSet the maximum thread slice runtime (in virtual cycles)")); quantum_sizer->Add(m_thread_quantum, 0, wxALL, 5); quantum_sizer->Add(new wxStaticText(box, wxID_ANY, _("cycles")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); first_row->Add(quantum_sizer, 0, wxEXPAND, 5); box_sizer->Add(first_row, 0, wxEXPAND, 5); sizer->Add(box_sizer, 0, wxEXPAND, 5); } panel->SetSizer(sizer); panel->Layout(); sizer->Fit(panel); m_notebook->AddPage(panel, _("General"), true); } // graphic { auto* panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); auto* sizer = new wxBoxSizer(wxVERTICAL); //m_extended_texture_readback = new wxCheckBox(panel, wxID_ANY, _("Extended texture readback")); //m_extended_texture_readback->SetToolTip(_("Improves emulation accuracy of CPU to GPU memory access at the cost of performance. Required for some games.")); //sizer->Add(m_extended_texture_readback, 0, wxALL, 5); auto* first_row = new wxFlexGridSizer(0, 2, 0, 0); first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); /*first_row->Add(new wxStaticText(panel, wxID_ANY, _("Precompiled shaders")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString precompiled_modes[] = { _("auto"), _("enable"), _("disable") }; const sint32 precompiled_count = std::size(precompiled_modes); m_precompiled = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, precompiled_count, precompiled_modes, 0); m_precompiled->SetToolTip(_("Precompiled shaders can speed up the load time on the shader loading screen.\nAuto will enable it for AMD/Intel but disable it for NVIDIA GPUs as a workaround for a driver bug.\n\nRecommended: Auto")); first_row->Add(m_precompiled, 0, wxALL, 5);*/ first_row->Add(new wxStaticText(panel, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString gapi_values[] = { "", "OpenGL", "Vulkan" }; m_graphic_api = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(gapi_values), gapi_values); first_row->Add(m_graphic_api, 0, wxALL, 5); first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader multiplication accuracy")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString mul_values[] = { _("false"), _("true"), _("minimal") }; m_shader_mul_accuracy = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(mul_values), mul_values); m_shader_mul_accuracy->SetToolTip(_("EXPERT OPTION\nControls the accuracy of floating point multiplication in shaders.\n\nRecommended: true")); first_row->Add(m_shader_mul_accuracy, 0, wxALL, 5); /*first_row->Add(new wxStaticText(panel, wxID_ANY, _("GPU buffer cache accuracy")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString accuarcy_values[] = { _("high"), _("medium"), _("low") }; m_cache_accuracy = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(accuarcy_values), accuarcy_values); m_cache_accuracy->SetToolTip(_("Lower value results in higher performance, but may cause graphical issues")); first_row->Add(m_cache_accuracy, 0, wxALL, 5);*/ sizer->Add(first_row, 0, wxEXPAND, 5); panel->SetSizer(sizer); panel->Layout(); sizer->Fit(panel); m_notebook->AddPage(panel, _("Graphic"), false); } //// audio //{ // auto panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); // wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); // m_disable_audio = new wxCheckBox(panel, wxID_ANY, _("Disable Audio"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER); // sizer->Add(m_disable_audio, 0, wxALL, 5); // panel->SetSizer(sizer); // panel->Layout(); // sizer->Fit(panel); // m_notebook->AddPage(panel, _("Audio"), false); //} // controller { auto panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); wxBoxSizer* sizer; sizer = new wxBoxSizer(wxVERTICAL); wxFlexGridSizer* profile_sizer; profile_sizer = new wxFlexGridSizer(0, 2, 0, 0); profile_sizer->SetFlexibleDirection(wxBOTH); profile_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); for (int i = 0; i < 8; ++i) { profile_sizer->Add(new wxStaticText(panel, wxID_ANY, fmt::format("{} {}", _("Controller").ToStdString(), (i + 1))), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_controller_profile[i] = new wxComboBox(panel, wxID_ANY,"", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_DROPDOWN| wxCB_READONLY); m_controller_profile[i]->SetMinSize(wxSize(250, -1)); m_controller_profile[i]->Bind(wxEVT_COMBOBOX_DROPDOWN, &GameProfileWindow::OnControllerProfileDropdown, this); m_controller_profile[i]->SetToolTip(_("Forces a given controller profile")); profile_sizer->Add(m_controller_profile[i], 0, wxALL, 5); } sizer->Add(profile_sizer, 0, wxEXPAND, 5); panel->SetSizer(sizer); panel->Layout(); sizer->Fit(panel); m_notebook->AddPage(panel, _("Controller"), false); } main_sizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5); this->SetSizer(main_sizer); this->Layout(); this->Centre(wxBOTH); ApplyProfile(); } GameProfileWindow::~GameProfileWindow() { SaveProfile(); } void GameProfileWindow::OnStreamoutSizeChange(wxCommandEvent& event) { wxSlider* slider = wxDynamicCast(event.GetEventObject(), wxSlider); wxASSERT(slider); wxStaticText* text = wxDynamicCast(slider->GetClientData(), wxStaticText); wxASSERT(text); text->SetLabelText(fmt::format("{} MB", slider->GetValue())); event.Skip(); } void GameProfileWindow::OnControllerProfileDropdown(wxCommandEvent& event) { wxComboBox* cb = wxDynamicCast(event.GetEventObject(), wxComboBox); wxASSERT(cb); wxWindowUpdateLocker lock(cb); const auto selected_value = cb->GetStringSelection(); cb->Clear(); cb->Append(wxEmptyString); auto profiles = InputManager::get_profiles(); for (const auto& profile : profiles) { cb->Append(to_wxString(profile)); } cb->SetStringSelection(selected_value); } void GameProfileWindow::SetProfileInt(gameProfileIntegerOption_t& option, wxCheckBox* checkbox, sint32 value) const { const auto state = checkbox->GetValue(); if (state) { option.isPresent = true; option.value = value; } else option.isPresent = false; } void GameProfileWindow::ApplyProfile() { if(m_game_profile.m_gameName) this->SetTitle(fmt::format("{} - {}", _("Edit game profile").ToStdString(), m_game_profile.m_gameName.value())); // general m_load_libs->SetValue(m_game_profile.m_loadSharedLibraries.value()); m_start_with_padview->SetValue(m_game_profile.m_startWithPadView); // cpu // wxString cpu_modes[] = { _("Singlecore-Interpreter"), _("Singlecore-Recompiler"), _("Triplecore-Recompiler"), _("Auto (recommended)") }; switch(m_game_profile.m_cpuMode.value()) { case CPUMode::SinglecoreInterpreter: m_cpu_mode->SetSelection(0); break; case CPUMode::SinglecoreRecompiler: m_cpu_mode->SetSelection(1); break; case CPUMode::DualcoreRecompiler: m_cpu_mode->SetSelection(2); break; case CPUMode::MulticoreRecompiler: m_cpu_mode->SetSelection(2); break; default: m_cpu_mode->SetSelection(3); } m_thread_quantum->SetStringSelection(fmt::format("{}", m_game_profile.m_threadQuantum)); // gpu if (!m_game_profile.m_graphics_api.has_value()) m_graphic_api->SetSelection(0); // selecting "" else m_graphic_api->SetSelection(1 + m_game_profile.m_graphics_api.value()); // "", OpenGL, Vulkan //m_extended_texture_readback->SetValue(m_game_profile.m_extendedTextureReadback); //m_precompiled->SetSelection((int)m_game_profile.m_precompiledShaders.value()); m_shader_mul_accuracy->SetSelection((int)m_game_profile.m_accurateShaderMul); //m_cache_accuracy->SetSelection((int)m_game_profile.m_gpuBufferCacheAccuracy.value()); //// audio //m_disable_audio->Set3StateValue(GetCheckboxState(m_game_profile.disableAudio)); // controller auto profiles = InputManager::get_profiles(); for (const auto& cb : m_controller_profile) { cb->Clear(); for (const auto& profile : profiles) { cb->Append(to_wxString(profile)); } } for (int i = 0; i < InputManager::kMaxController; ++i) { const bool has_value = m_game_profile.m_controllerProfile[i].has_value(); if (has_value) { const auto& v = m_game_profile.m_controllerProfile[i].value(); m_controller_profile[i]->SetStringSelection(wxString::FromUTF8(v)); } else m_controller_profile[i]->SetSelection(wxNOT_FOUND); } } void GameProfileWindow::SaveProfile() { // update game profile struct m_game_profile.Reset(); // general m_game_profile.m_loadSharedLibraries = m_load_libs->GetValue(); m_game_profile.m_startWithPadView = m_start_with_padview->GetValue(); // cpu switch(m_cpu_mode->GetSelection()) { case 0: m_game_profile.m_cpuMode = CPUMode::SinglecoreInterpreter; break; case 1: m_game_profile.m_cpuMode = CPUMode::SinglecoreRecompiler; break; case 2: m_game_profile.m_cpuMode = CPUMode::MulticoreRecompiler; break; default: m_game_profile.m_cpuMode = CPUMode::Auto; } const wxString thread_quantum = m_thread_quantum->GetStringSelection(); if (!thread_quantum.empty()) { m_game_profile.m_threadQuantum = ConvertString(thread_quantum.ToStdString()); m_game_profile.m_threadQuantum = std::min(m_game_profile.m_threadQuantum, 536870912); m_game_profile.m_threadQuantum = std::max(m_game_profile.m_threadQuantum, 5000); } // gpu m_game_profile.m_accurateShaderMul = (AccurateShaderMulOption)m_shader_mul_accuracy->GetSelection(); if (m_graphic_api->GetSelection() == 0) m_game_profile.m_graphics_api = {}; else m_game_profile.m_graphics_api = (GraphicAPI)(m_graphic_api->GetSelection() - 1); // "", OpenGL, Vulkan // controller for (int i = 0; i < 8; ++i) { if(m_controller_profile[i]->GetSelection() == wxNOT_FOUND) { m_game_profile.m_controllerProfile[i].reset(); continue; } const wxString profile_name = m_controller_profile[i]->GetStringSelection(); if (profile_name.empty()) m_game_profile.m_controllerProfile[i].reset(); else m_game_profile.m_controllerProfile[i] = profile_name.ToUTF8(); } // update game profile file m_game_profile.Save(m_title_id); } void GameProfileWindow::SetSliderValue(wxSlider* slider, sint32 new_value) const { wxASSERT(slider); slider->SetValue(new_value); wxCommandEvent slider_event(wxEVT_SLIDER, slider->GetId()); slider_event.SetEventObject(slider); slider_event.SetClientData((void*)IsFrozen()); wxPostEvent(slider->GetEventHandler(), slider_event); }