Add Metal backend (#1287)

This commit is contained in:
SamoZ256 2025-12-06 17:14:25 +01:00 committed by GitHub
parent 5520613dc3
commit 26e40a4bce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
108 changed files with 14986 additions and 453 deletions

View file

@ -116,6 +116,13 @@ add_library(CemuWxGui STATIC
wxHelper.h
)
if (ENABLE_METAL)
target_sources(CemuWxGui PRIVATE
canvas/MetalCanvas.cpp
canvas/MetalCanvas.h
)
endif()
if (ENABLE_BLUEZ)
target_sources(CemuWxGui PRIVATE
input/PairingDialog.cpp

View file

@ -595,5 +595,3 @@ void CemuApp::ActivateApp(wxActivateEvent& event)
g_window_info.app_active = event.GetActive();
event.Skip();
}

View file

@ -61,7 +61,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
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(m_cpu_mode, 0, wxALL, 5);
first_row->Add(new wxStaticText(box, wxID_ANY, _("Thread quantum")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
@ -112,10 +112,14 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxString gapi_values[] = { "", "OpenGL", "Vulkan" };
wxString gapi_values[] = { "", "OpenGL", "Vulkan",
#if ENABLE_METAL
"Metal"
#endif
};
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")};
@ -123,6 +127,27 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
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, _("Shader fast math")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxString math_values[] = { _("false"), _("true") };
m_shader_fast_math = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(math_values), math_values);
m_shader_fast_math->SetToolTip(_("EXPERT OPTION\nEnables fast math for all shaders. May (rarely) cause graphical bugs.\n\nMetal only\n\nRecommended: true"));
first_row->Add(m_shader_fast_math, 0, wxALL, 5);
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Metal buffer cache mode")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxString cache_values[] = { _("auto"), _("device private"), _("device shared"), _("host") };
m_metal_buffer_cache_mode = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(cache_values), cache_values);
m_metal_buffer_cache_mode->SetToolTip(_("EXPERT OPTION\nDecides how the buffer cache memory will be managed.\n\nMetal only\n\nRecommended: auto"));
first_row->Add(m_metal_buffer_cache_mode, 0, wxALL, 5);
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Position invariance")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxString pos_values[] = { _("auto"), _("false"), _("true") };
m_position_invariance = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(pos_values), pos_values);
m_position_invariance->SetToolTip(_("EXPERT OPTION\nDisables most optimizations for vertex positions. May fix polygon cutouts or flickering in some games.\n\nMetal only\n\nRecommended: auto"));
first_row->Add(m_position_invariance, 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);
@ -249,7 +274,7 @@ void GameProfileWindow::ApplyProfile()
// 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())
@ -258,24 +283,27 @@ void GameProfileWindow::ApplyProfile()
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);
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_graphic_api->SetSelection(1 + m_game_profile.m_graphics_api.value()); // "", OpenGL, Vulkan, Metal
m_shader_mul_accuracy->SetSelection((int)m_game_profile.m_accurateShaderMul);
m_shader_fast_math->SetSelection((int)m_game_profile.m_shaderFastMath);
m_metal_buffer_cache_mode->SetSelection((int)m_game_profile.m_metalBufferCacheMode);
m_position_invariance->SetSelection((int)m_game_profile.m_positionInvariance);
//// 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();
@ -293,7 +321,7 @@ void GameProfileWindow::ApplyProfile()
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);
}
@ -317,7 +345,7 @@ void GameProfileWindow::SaveProfile()
m_game_profile.m_cpuMode = CPUMode::Auto;
}
const wxString thread_quantum = m_thread_quantum->GetStringSelection();
if (!thread_quantum.empty())
{
@ -330,11 +358,14 @@ void GameProfileWindow::SaveProfile()
m_game_profile.m_accurateShaderMul = (AccurateShaderMulOption)m_shader_mul_accuracy->GetSelection();
if (m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::False && m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::True)
m_game_profile.m_accurateShaderMul = AccurateShaderMulOption::True; // force a legal value
m_game_profile.m_shaderFastMath = (bool)m_shader_fast_math->GetSelection();
m_game_profile.m_metalBufferCacheMode = (MetalBufferCacheMode)m_metal_buffer_cache_mode->GetSelection();
m_game_profile.m_positionInvariance = (PositionInvariance)m_position_invariance->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
m_game_profile.m_graphics_api = (GraphicAPI)(m_graphic_api->GetSelection() - 1); // "", OpenGL, Vulkan, Metal
// controller
for (int i = 0; i < 8; ++i)

View file

@ -40,6 +40,9 @@ private:
wxChoice* m_graphic_api;
wxChoice* m_shader_mul_accuracy;
wxChoice* m_shader_fast_math;
wxChoice* m_metal_buffer_cache_mode;
wxChoice* m_position_invariance;
//wxChoice* m_cache_accuracy;
// audio
@ -47,4 +50,4 @@ private:
// controller
wxComboBox* m_controller_profile[8];
};
};

View file

@ -11,6 +11,7 @@
#include <wx/collpane.h>
#include <wx/clrpicker.h>
#include <wx/cshelp.h>
#include <wx/textctrl.h>
#include <wx/textdlg.h>
#include <wx/hyperlink.h>
@ -28,6 +29,9 @@
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
#if ENABLE_METAL
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
#endif
#include "Cafe/Account/Account.h"
#include <boost/tokenizer.hpp>
@ -94,6 +98,19 @@ private:
VulkanRenderer::DeviceInfo m_device_info;
};
#if ENABLE_METAL
class wxMetalUUID : public wxClientData
{
public:
wxMetalUUID(const MetalRenderer::DeviceInfo& info)
: m_device_info(info) {}
const MetalRenderer::DeviceInfo& GetDeviceInfo() const { return m_device_info; }
private:
MetalRenderer::DeviceInfo m_device_info;
};
#endif
class wxAccountData : public wxClientData
{
public:
@ -102,7 +119,7 @@ public:
Account& GetAccount() { return m_account; }
const Account& GetAccount() const { return m_account; }
private:
Account m_account;
};
@ -336,12 +353,14 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
row->Add(new wxStaticText(box, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
sint32 api_size = 1;
wxString choices[2] = { "OpenGL" };
wxString choices[3] = { "OpenGL" };
if (g_vulkan_available)
{
choices[1] = "Vulkan";
api_size = 2;
choices[api_size++] = "Vulkan";
}
#if ENABLE_METAL
choices[api_size++] = "Metal";
#endif
m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices);
m_graphic_api->SetSelection(0);
@ -373,6 +392,10 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance"));
graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5);
m_force_mesh_shaders = new wxCheckBox(box, wxID_ANY, _("Force mesh shaders"));
m_force_mesh_shaders->SetToolTip(_("Force mesh shaders on all GPUs that support them. Mesh shaders are disabled by default on Intel GPUs due to potential stability issues"));
graphic_misc_row->Add(m_force_mesh_shaders, 0, wxALL, 5);
box_sizer->Add(graphic_misc_row, 1, wxEXPAND, 5);
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
@ -867,7 +890,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
auto* row = new wxFlexGridSizer(0, 2, 0, 0);
row->SetFlexibleDirection(wxBOTH);
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
const wxImage tmp = wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage();
m_validate_online = new wxBitmapButton(box, wxID_ANY, tmp.Scale(16, 16));
m_validate_online->Bind(wxEVT_BUTTON, &GeneralSettings2::OnShowOnlineValidator, this);
@ -877,7 +900,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
row->Add(m_online_status, 1, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
box_sizer->Add(row, 1, wxEXPAND, 5);
auto* tutorial_link = new wxHyperlinkCtrl(box, wxID_ANY, _("Online play tutorial"), "https://cemu.info/online-guide");
box_sizer->Add(tutorial_link, 0, wxALL, 5);
@ -977,6 +1000,33 @@ wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook)
debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5);
}
{
auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0);
debug_row->SetFlexibleDirection(wxBOTH);
debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
debug_row->Add(new wxStaticText(panel, wxID_ANY, _("GPU capture save directory"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
m_gpu_capture_dir = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_DONTWRAP);
m_gpu_capture_dir->SetMinSize(wxSize(150, -1));
m_gpu_capture_dir->SetToolTip(_("Cemu will save the GPU captures done by selecting Debug -> GPU capture in the menu bar in this directory. If a debugger with support for GPU captures (like Xcode) is attached, the capture will be opened in that debugger instead. If such debugger is not attached, METAL_CAPTURE_ENABLED must be set to 1 as an environment variable."));
debug_row->Add(m_gpu_capture_dir, 0, wxALL | wxEXPAND, 5);
debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5);
}
{
auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0);
debug_row->SetFlexibleDirection(wxBOTH);
debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
m_framebuffer_fetch = new wxCheckBox(panel, wxID_ANY, _("Framebuffer fetch"));
m_framebuffer_fetch->SetToolTip(_("Enable framebuffer fetch for eligible textures on supported devices."));
debug_row->Add(m_framebuffer_fetch, 0, wxALL | wxEXPAND, 5);
debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5);
}
panel->SetSizerAndFit(debug_panel_sizer);
return panel;
@ -992,14 +1042,14 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
notebook->AddPage(AddGeneralPage(notebook), _("General"));
notebook->AddPage(AddGraphicsPage(notebook), _("Graphics"));
notebook->AddPage(AddAudioPage(notebook), _("Audio"));
notebook->AddPage(AddAudioPage(notebook), _("Audio"));
notebook->AddPage(AddOverlayPage(notebook), _("Overlay"));
notebook->AddPage(AddAccountPage(notebook), _("Account"));
notebook->AddPage(AddDebugPage(notebook), _("Debug"));
Bind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this);
//
//
sizer->Add(notebook, 1, wxEXPAND | wxALL, 5);
@ -1014,7 +1064,7 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
ApplyConfig();
HandleGraphicsApiSelection();
DisableSettings(game_launched);
}
@ -1026,7 +1076,7 @@ uint32 GeneralSettings2::GetSelectedAccountPersistentId()
return dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
}
void GeneralSettings2::StoreConfig()
void GeneralSettings2::StoreConfig()
{
auto* app = (CemuApp*)wxTheApp;
auto& config = GetConfig();
@ -1051,7 +1101,6 @@ void GeneralSettings2::StoreConfig()
ScreenSaver::SetInhibit(config.disable_screensaver);
}
// -1 is default wx widget value -> set to dummy 0 so mainwindow and padwindow will update it
wxGuiConfig.window_position = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
wxGuiConfig.window_size = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
@ -1094,7 +1143,7 @@ void GeneralSettings2::StoreConfig()
config.pad_channels = kStereo; // (AudioChannels)m_pad_channels->GetSelection();
//config.input_channels = (AudioChannels)m_input_channels->GetSelection();
config.input_channels = kMono; // (AudioChannels)m_input_channels->GetSelection();
config.tv_volume = m_tv_volume->GetValue();
config.pad_volume = m_pad_volume->GetValue();
config.input_volume = m_input_volume->GetValue();
@ -1140,29 +1189,48 @@ void GeneralSettings2::StoreConfig()
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
selection = m_graphic_device->GetSelection();
if(selection != wxNOT_FOUND)
if (config.graphic_api == GraphicAPI::kVulkan)
{
const auto* info = (wxVulkanUUID*)m_graphic_device->GetClientObject(selection);
if(info)
config.graphic_device_uuid = info->GetDeviceInfo().uuid;
else
config.graphic_device_uuid = {};
if (selection != wxNOT_FOUND)
{
const auto* info = (wxVulkanUUID*)m_graphic_device->GetClientObject(selection);
if (info)
config.vk_graphic_device_uuid = info->GetDeviceInfo().uuid;
else
config.vk_graphic_device_uuid = {};
}
else
config.vk_graphic_device_uuid = {};
}
else
config.graphic_device_uuid = {};
else if (config.graphic_api == GraphicAPI::kMetal)
{
if (selection != wxNOT_FOUND)
{
#if ENABLE_METAL
const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection);
if (info)
config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid;
else
config.mtl_graphic_device_uuid = {};
#endif
}
else
config.mtl_graphic_device_uuid = {};
}
config.vsync = m_vsync->GetSelection();
config.overrideAppGammaPreference = m_overrideGamma->IsChecked();
config.overrideGammaValue = m_overrideGammaValue->GetValue();
config.userDisplayGamma = m_userDisplayGamma->GetValue() * !m_userDisplayisSRGB->GetValue();
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
config.force_mesh_shaders = m_force_mesh_shaders->IsChecked();
config.async_compile = m_async_compile->IsChecked();
config.upscale_filter = m_upscale_filter->GetSelection();
config.downscale_filter = m_downscale_filter->GetSelection();
config.fullscreen_scaling = m_fullscreen_scaling->GetSelection();
config.overlay.position = (ScreenPosition)m_overlay_position->GetSelection(); wxASSERT((int)config.overlay.position <= (int)ScreenPosition::kBottomRight);
config.overlay.text_color = m_overlay_font_color->GetColour().GetRGBA();
config.overlay.text_scale = m_overlay_scale->GetSelection() * 25 + 50;
@ -1189,6 +1257,8 @@ void GeneralSettings2::StoreConfig()
// debug
config.crash_dump = (CrashDump)m_crash_dump->GetSelection();
config.gdb_port = m_gdb_port->GetValue();
config.gpu_capture_dir = m_gpu_capture_dir->GetValue().utf8_string();
config.framebuffer_fetch = m_framebuffer_fetch->IsChecked();
GetConfigHandle().Save();
}
@ -1230,7 +1300,7 @@ void GeneralSettings2::OnAudioLatencyChanged(wxCommandEvent& event)
void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
{
if(event.GetEventObject() == m_input_volume)
{
std::shared_lock lock(g_audioInputMutex);
@ -1258,7 +1328,7 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
g_portalAudio->SetVolume(event.GetInt());
}
}
event.Skip();
}
@ -1351,7 +1421,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
// todo reset global instance of audio device
}
void GeneralSettings2::ResetAccountInformation()
void GeneralSettings2::ResetAccountInformation()
{
m_account_grid->SetSplitterPosition(100);
m_active_account->SetSelection(0);
@ -1379,7 +1449,7 @@ void GeneralSettings2::OnAccountCreate(wxCommandEvent& event)
Account account(dialog.GetPersistentId(), dialog.GetMiiName().ToStdWstring());
account.Save();
Account::RefreshAccounts();
const int index = m_active_account->Append(account.ToString(), new wxAccountData(account));
// update ui
@ -1388,7 +1458,7 @@ void GeneralSettings2::OnAccountCreate(wxCommandEvent& event)
m_create_account->Enable(m_active_account->GetCount() < 0xC);
m_delete_account->Enable(m_active_account->GetCount() > 1);
// send main window event
wxASSERT(GetParent());
wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH);
@ -1418,7 +1488,7 @@ void GeneralSettings2::OnAccountDelete(wxCommandEvent& event)
return;
// todo: ask if saves should be deleted too?
const fs::path path = account.GetFileName();
try
{
@ -1436,7 +1506,7 @@ void GeneralSettings2::OnAccountDelete(wxCommandEvent& event)
SystemException sys(ex);
cemuLog_log(LogType::Force, sys.what());
}
}
void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
@ -1491,7 +1561,7 @@ void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
else if (property->GetName() == kPropertyEmail)
{
account.SetEmail(value.As<wxString>().ToStdString());
}
else if (property->GetName() == kPropertyCountry)
{
@ -1499,7 +1569,7 @@ void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
}
else
cemu_assert_debug(false);
account.Save();
Account::RefreshAccounts(); // refresh internal account list
UpdateAccountInformation(); // refresh on invalid values
@ -1539,7 +1609,7 @@ void GeneralSettings2::UpdateAccountInformation()
gender_property->SetChoiceSelection(std::min(gender_property->GetChoices().GetCount() - 1, (uint32)account.GetGender()));
m_account_grid->GetProperty(kPropertyEmail)->SetValueFromString(std::string{ account.GetEmail() });
auto* country_property = dynamic_cast<wxEnumProperty*>(m_account_grid->GetProperty(kPropertyCountry));
wxASSERT(country_property);
int index = (country_property)->GetIndexForValue(account.GetCountry());
@ -1623,9 +1693,9 @@ void GeneralSettings2::HandleGraphicsApiSelection()
int selection = m_vsync->GetSelection();
if(selection == wxNOT_FOUND)
selection = GetConfig().vsync;
m_vsync->Clear();
if(m_graphic_api->GetSelection() == 0)
if (m_graphic_api->GetSelection() == 0)
{
// OpenGL
m_vsync->AppendString(_("Off"));
@ -1640,12 +1710,14 @@ void GeneralSettings2::HandleGraphicsApiSelection()
m_gx2drawdone_sync->Enable();
m_async_compile->Disable();
m_force_mesh_shaders->Disable();
}
else
else if (m_graphic_api->GetSelection() == 1)
{
// Vulkan
m_gx2drawdone_sync->Disable();
m_async_compile->Enable();
m_force_mesh_shaders->Disable();
m_vsync->AppendString(_("Off"));
m_vsync->AppendString(_("Double buffering"));
@ -1655,7 +1727,7 @@ void GeneralSettings2::HandleGraphicsApiSelection()
#endif
m_vsync->Select(selection);
m_graphic_device->Enable();
auto devices = VulkanRenderer::GetDevices();
m_graphic_device->Clear();
@ -1670,7 +1742,7 @@ void GeneralSettings2::HandleGraphicsApiSelection()
const auto& config = GetConfig();
for(size_t i = 0; i < devices.size(); ++i)
{
if(config.graphic_device_uuid == devices[i].uuid)
if(config.vk_graphic_device_uuid == devices[i].uuid)
{
m_graphic_device->SetSelection(i);
break;
@ -1678,6 +1750,42 @@ void GeneralSettings2::HandleGraphicsApiSelection()
}
}
}
else
{
// Metal
m_gx2drawdone_sync->Disable();
m_async_compile->Enable();
m_force_mesh_shaders->Enable();
m_vsync->AppendString(_("Off"));
m_vsync->AppendString(_("On"));
m_vsync->Select(selection);
m_graphic_device->Enable();
m_graphic_device->Clear();
#if ENABLE_METAL
auto devices = MetalRenderer::GetDevices();
if(!devices.empty())
{
for (const auto& device : devices)
{
m_graphic_device->Append(device.name, new wxMetalUUID(device));
}
m_graphic_device->SetSelection(0);
const auto& config = GetConfig();
for (size_t i = 0; i < devices.size(); ++i)
{
if (config.mtl_graphic_device_uuid == devices[i].uuid)
{
m_graphic_device->SetSelection(i);
break;
}
}
}
#endif
}
}
void GeneralSettings2::ApplyConfig()
@ -1744,6 +1852,7 @@ void GeneralSettings2::ApplyConfig()
}
m_async_compile->SetValue(config.async_compile);
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
m_force_mesh_shaders->SetValue(config.force_mesh_shaders);
m_upscale_filter->SetSelection(config.upscale_filter);
m_downscale_filter->SetSelection(config.downscale_filter);
m_fullscreen_scaling->SetSelection(config.fullscreen_scaling);
@ -1794,7 +1903,7 @@ void GeneralSettings2::ApplyConfig()
m_pad_channels->SetSelection(0);
//m_input_channels->SetSelection(config.pad_channels);
m_input_channels->SetSelection(0);
SendSliderEvent(m_tv_volume, config.tv_volume);
if (!config.tv_device.empty() && m_tv_device->HasClientObjectData())
@ -1811,7 +1920,7 @@ void GeneralSettings2::ApplyConfig()
}
else
m_tv_device->SetSelection(0);
SendSliderEvent(m_pad_volume, config.pad_volume);
if (!config.pad_device.empty() && m_pad_device->HasClientObjectData())
{
@ -1880,6 +1989,8 @@ void GeneralSettings2::ApplyConfig()
// debug
m_crash_dump->SetSelection((int)config.crash_dump.GetValue());
m_gdb_port->SetValue(config.gdb_port.GetValue());
m_gpu_capture_dir->SetValue(wxHelper::FromUtf8(config.gpu_capture_dir.GetValue()));
m_framebuffer_fetch->SetValue(config.framebuffer_fetch);
}
void GeneralSettings2::OnAudioAPISelected(wxCommandEvent& event)
@ -1947,7 +2058,7 @@ void GeneralSettings2::UpdateAudioDevice()
}
}
}
// pad audio device
{
const auto selection = m_pad_device->GetSelection();
@ -2041,7 +2152,7 @@ void GeneralSettings2::UpdateAudioDevice()
channels = g_portalAudio->GetChannels();
else
channels = 1;
try
{
g_portalAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 8000, 1, 32, 16);
@ -2074,14 +2185,14 @@ void GeneralSettings2::OnAudioChannelsSelected(wxCommandEvent& event)
{
if (config.tv_channels == (AudioChannels)obj->GetSelection())
return;
config.tv_channels = (AudioChannels)obj->GetSelection();
}
else if (obj == m_pad_channels)
{
if (config.pad_channels == (AudioChannels)obj->GetSelection())
return;
config.pad_channels = (AudioChannels)obj->GetSelection();
}
else
@ -2233,23 +2344,23 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
const auto selection = m_active_account->GetSelection();
if (selection == wxNOT_FOUND)
return;
const auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
wxASSERT(obj);
const auto& account = obj->GetAccount();
const auto validator = account.ValidateOnlineFiles();
if (validator) // everything valid? shouldn't happen
return;
wxString err;
err << _("The following error(s) have been found:") << '\n';
if (validator.otp == OnlineValidator::FileState::Missing)
err << _("otp.bin missing in Cemu directory") << '\n';
else if(validator.otp == OnlineValidator::FileState::Corrupted)
err << _("otp.bin is invalid") << '\n';
if (validator.seeprom == OnlineValidator::FileState::Missing)
err << _("seeprom.bin missing in Cemu directory") << '\n';
else if(validator.seeprom == OnlineValidator::FileState::Corrupted)

View file

@ -28,7 +28,7 @@ private:
bool m_has_account_change = false; // keep track of dirty state of accounts
wxPanel* AddGeneralPage(wxNotebook* notebook);
wxPanel* AddGraphicsPage(wxNotebook* notebook);
wxPanel* AddAudioPage(wxNotebook* notebook);
@ -61,7 +61,7 @@ private:
wxSpinCtrlDouble* m_userDisplayGamma;
wxCheckBox* m_userDisplayisSRGB;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync, *m_force_mesh_shaders;
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale;
wxCheckBox* m_controller_profile_name, *m_controller_low_battery, *m_shader_compiling, *m_friends_data;
@ -87,6 +87,8 @@ private:
// Debug
wxChoice* m_crash_dump;
wxSpinCtrl* m_gdb_port;
wxTextCtrl* m_gpu_capture_dir;
wxCheckBox* m_framebuffer_fetch;
void OnAccountCreate(wxCommandEvent& event);
void OnAccountDelete(wxCommandEvent& event);
@ -116,11 +118,10 @@ private:
void UpdateAudioDevice();
// refreshes audio device list for dropdown
void UpdateAudioDeviceList();
void ResetAccountInformation();
void UpdateAccountInformation();
void UpdateOnlineAccounts();
void HandleGraphicsApiSelection();
void ApplyConfig();
};

View file

@ -18,7 +18,7 @@ LoggingWindow::LoggingWindow(wxFrame* parent)
filter_row->Add(new wxStaticText( this, wxID_ANY, _("Filter")), 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
wxString choices[] = {"Unsupported APIs calls", "Coreinit Logging", "Coreinit File-Access", "Coreinit Thread-Synchronization", "Coreinit Memory", "Coreinit MP", "Coreinit Thread", "nn::nfp", "GX2", "Audio", "Input", "Socket", "Save", "H264", "Graphic pack patches", "Texture cache", "Texture readback", "OpenGL debug output", "Vulkan validation layer"};
wxString choices[] = {"Unsupported APIs calls", "Coreinit Logging", "Coreinit File-Access", "Coreinit Thread-Synchronization", "Coreinit Memory", "Coreinit MP", "Coreinit Thread", "nn::nfp", "GX2", "Audio", "Input", "Socket", "Save", "H264", "Graphic pack patches", "Texture cache", "Texture readback", "OpenGL debug output", "Vulkan validation layer", "Metal debug output"};
m_filter = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 0 );
m_filter->Bind(wxEVT_COMBOBOX, &LoggingWindow::OnFilterChange, this);
m_filter->Bind(wxEVT_TEXT, &LoggingWindow::OnFilterChange, this);
@ -69,7 +69,7 @@ void LoggingWindow::Log(std::string_view filter, std::wstring_view message)
void LoggingWindow::OnLogMessage(wxLogEvent& event)
{
m_log_list->PushEntry(event.GetFilter(), event.GetMessage());
m_log_list->PushEntry(event.GetFilter(), event.GetMessage());
}
void LoggingWindow::OnFilterChange(wxCommandEvent& event)
@ -83,4 +83,3 @@ void LoggingWindow::OnFilterMessageChange(wxCommandEvent& event)
m_log_list->SetFilterMessage(m_filter_message->GetValue());
event.Skip();
}

View file

@ -14,6 +14,7 @@
#include "AudioDebuggerWindow.h"
#include "wxgui/canvas/OpenGLCanvas.h"
#include "wxgui/canvas/VulkanCanvas.h"
#include "wxgui/canvas/MetalCanvas.h"
#include "Cafe/OS/libs/nfc/nfc.h"
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "wxgui/debugger/DebuggerWindow2.h"
@ -64,6 +65,10 @@
#include "gamemode_client.h"
#endif
#if ENABLE_METAL
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
#endif
#include "Cafe/TitleList/TitleInfo.h"
#include "Cafe/TitleList/TitleList.h"
#include "wxHelper.h"
@ -100,7 +105,7 @@ enum
// options -> account
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
// options -> system language
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE = 20500,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH,
@ -143,6 +148,7 @@ enum
MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS,
MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY,
MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS,
MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE,
// debug->logging
MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE = 21499,
@ -225,6 +231,7 @@ EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_FST, MainWindow::OnDebugSetting)
// debug -> View ...
@ -257,7 +264,7 @@ public:
{
if(!m_window->IsGameLaunched() && filenames.GetCount() == 1)
return m_window->FileLoad(_utf8ToPath(filenames[0].utf8_string()), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP);
return false;
}
@ -473,7 +480,7 @@ bool MainWindow::InstallUpdate(const fs::path& metaFilePath)
{
throw std::runtime_error(frame.GetExceptionMessage());
}
}
}
}
catch(const AbortException&)
{
@ -657,13 +664,13 @@ void MainWindow::OnFileMenu(wxCommandEvent& event)
_("Wii U executable (*.rpx, *.elf)"),
_("All files (*.*)")
);
wxFileDialog openFileDialog(this, _("Open file to launch"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL || openFileDialog.GetPath().IsEmpty())
return;
const wxString wxStrFilePath = openFileDialog.GetPath();
const wxString wxStrFilePath = openFileDialog.GetPath();
FileLoad(_utf8ToPath(wxStrFilePath.utf8_string()), wxLaunchGameEvent::INITIATED_BY::MENU);
}
else if (menuId >= MAINFRAME_MENU_ID_FILE_RECENT_0 && menuId <= MAINFRAME_MENU_ID_FILE_RECENT_LAST)
@ -814,7 +821,7 @@ void MainWindow::TogglePadView()
{
if (m_padView)
return;
m_padView = new PadViewFrame(this);
m_padView->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnPadClose, this);
@ -1029,7 +1036,7 @@ void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
// GetConfig().cpu_mode = CPUMode::TriplecoreRecompiler;
// else
// cemu_assert_debug(false);
//
//
// GetConfigHandle().Save();
//}
@ -1043,6 +1050,14 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event)
if(!GetConfig().vk_accurate_barriers)
wxMessageBox(_("Warning: Disabling the accurate barriers option will lead to flickering graphics but may improve performance. It is highly recommended to leave it turned on."), _("Accurate barriers are off"), wxOK);
}
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE)
{
cemu_assert_debug(g_renderer->GetType() == RendererAPI::Metal);
#if ENABLE_METAL
static_cast<MetalRenderer*>(g_renderer.get())->CaptureFrame();
#endif
}
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY)
ActiveSettings::EnableAudioOnlyAux(event.IsChecked());
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_RAM)
@ -1093,7 +1108,7 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event)
ActiveSettings::SetTimerShiftFactor(6);
else
cemu_assert_debug(false);
GetConfigHandle().Save();
}
@ -1165,7 +1180,7 @@ void MainWindow::OnLoggingWindow(wxCommandEvent& event)
return;
m_logging_window = new LoggingWindow(this);
m_logging_window->Bind(wxEVT_CLOSE_WINDOW,
m_logging_window->Bind(wxEVT_CLOSE_WINDOW,
[this](wxCloseEvent& event) {
m_logging_window = nullptr;
event.Skip();
@ -1340,7 +1355,7 @@ void MainWindow::SaveSettings()
{
auto lock = GetConfigHandle().Lock();
auto& config = GetWxGUIConfig();
if (config.window_position != Vector2i{ -1,-1 })
{
config.window_position.x = m_restored_position.x;
@ -1377,7 +1392,7 @@ void MainWindow::SaveSettings()
if(m_game_list)
m_game_list->SaveConfig();
g_wxConfig.Save();
}
@ -1407,14 +1422,14 @@ void MainWindow::OnMouseMove(wxMouseEvent& event)
void MainWindow::OnMouseLeft(wxMouseEvent& event)
{
auto& instance = InputManager::instance();
std::scoped_lock lock(instance.m_main_mouse.m_mutex);
instance.m_main_mouse.left_down = event.ButtonDown(wxMOUSE_BTN_LEFT);
auto physPos = ToPhys(event.GetPosition());
instance.m_main_mouse.position = { physPos.x, physPos.y };
if (event.ButtonDown(wxMOUSE_BTN_LEFT))
instance.m_main_mouse.left_down_toggle = true;
event.Skip();
}
@ -1428,7 +1443,7 @@ void MainWindow::OnMouseRight(wxMouseEvent& event)
instance.m_main_mouse.position = { physPos.x, physPos.y };
if(event.ButtonDown(wxMOUSE_BTN_RIGHT))
instance.m_main_mouse.right_down_toggle = true;
event.Skip();
}
@ -1485,7 +1500,7 @@ void MainWindow::OnKeyDown(wxKeyEvent& event)
#endif
else
{
event.Skip();
event.Skip();
}
}
@ -1493,7 +1508,7 @@ void MainWindow::OnChar(wxKeyEvent& event)
{
if (swkbd_hasKeyboardInputHook())
swkbd_keyInput(event.GetUnicodeKey());
// event.Skip();
}
@ -1518,7 +1533,7 @@ void MainWindow::OnToolsInput(wxCommandEvent& event)
case MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER:
{
const auto default_tab = id == MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER ? TitleManagerPage::TitleManager : TitleManagerPage::DownloadManager;
if (m_title_manager)
m_title_manager->SetFocusAndTab(default_tab);
else
@ -1568,7 +1583,7 @@ void MainWindow::OnGesturePan(wxPanGestureEvent& event)
instance.m_main_touch.left_down = event.IsGestureStart() || !event.IsGestureEnd();
if (event.IsGestureStart() || !event.IsGestureEnd())
instance.m_main_touch.left_down_toggle = true;
event.Skip();
}
@ -1602,8 +1617,12 @@ void MainWindow::CreateCanvas()
// create canvas
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
else
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true);
#if ENABLE_METAL
else
m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true);
#endif
// mouse events
m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this);
@ -1783,10 +1802,10 @@ void MainWindow::UpdateNFCMenu()
const auto& entry = config.recent_nfc_files[i];
if (entry.empty())
continue;
if (!fs::exists(_utf8ToPath(entry)))
continue;
if (recentFileIndex == 0)
m_nfcMenuSeparator0 = m_nfcMenu->AppendSeparator();
@ -1837,7 +1856,7 @@ void MainWindow::OnTimer(wxTimerEvent& event)
{
ShowCursor(false);
}
}
#define BUILD_DATE __DATE__ " " __TIME__
@ -2106,9 +2125,9 @@ void MainWindow::RecreateMenu()
m_menuBar->Destroy();
m_menuBar = nullptr;
}
auto& guiConfig = GetWxGUIConfig();
m_menuBar = new wxMenuBar();
// file submenu
m_fileMenu = new wxMenu();
@ -2171,7 +2190,7 @@ void MainWindow::RecreateMenu()
item->Check(account_id == account.GetPersistentId());
if (m_game_launched || LaunchSettings::GetPersistentId().has_value())
item->Enable(false);
++index;
}
@ -2203,8 +2222,8 @@ void MainWindow::RecreateMenu()
// options submenu
wxMenu* optionsMenu = new wxMenu();
m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen"), wxEmptyString);
m_fullscreenMenuItem->Check(FullscreenEnabled());
m_fullscreenMenuItem->Check(FullscreenEnabled());
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs"));
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
m_padViewMenuItem->Check(wxConfig.pad_open);
@ -2303,7 +2322,7 @@ void MainWindow::RecreateMenu()
debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging"));
debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump"));
debugMenu->AppendSeparator();
auto upsidedownItem = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, _("&Render upside-down"), wxEmptyString);
upsidedownItem->Check(ActiveSettings::RenderUpsideDownEnabled());
if(LaunchSettings::RenderUpsideDownEnabled().has_value())
@ -2312,6 +2331,9 @@ void MainWindow::RecreateMenu()
auto accurateBarriers = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, _("&Accurate barriers (Vulkan)"), wxEmptyString);
accurateBarriers->Check(GetConfig().vk_accurate_barriers);
auto gpuCapture = debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, _("&GPU capture (Metal)"));
gpuCapture->Enable(m_game_launched && g_renderer->GetType() == RendererAPI::Metal);
debugMenu->AppendSeparator();
#ifdef CEMU_DEBUG_ASSERT

View file

@ -8,6 +8,7 @@
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "wxgui/canvas/OpenGLCanvas.h"
#include "wxgui/canvas/VulkanCanvas.h"
#include "wxgui/canvas/MetalCanvas.h"
#include "config/CemuConfig.h"
#include "wxgui/MainWindow.h"
#include "wxgui/helpers/wxHelpers.h"
@ -74,8 +75,12 @@ void PadViewFrame::InitializeRenderCanvas()
{
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false);
else
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false);
#if ENABLE_METAL
else
m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false);
#endif
sizer->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr);
}
SetSizer(sizer);
@ -173,7 +178,7 @@ void PadViewFrame::OnChar(wxKeyEvent& event)
{
if (swkbd_hasKeyboardInputHook())
swkbd_keyInput(event.GetUnicodeKey());
event.Skip();
}
@ -198,7 +203,7 @@ void PadViewFrame::OnMouseLeft(wxMouseEvent& event)
instance.m_pad_mouse.position = { physPos.x, physPos.y };
if (event.ButtonDown(wxMOUSE_BTN_LEFT))
instance.m_pad_mouse.left_down_toggle = true;
}
void PadViewFrame::OnMouseRight(wxMouseEvent& event)

View file

@ -0,0 +1,61 @@
#include "wxgui/canvas/MetalCanvas.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
#include <wx/msgdlg.h>
#include <helpers/wxHelpers.h>
MetalCanvas::MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window)
: IRenderCanvas(is_main_window), wxWindow(parent, wxID_ANY, wxDefaultPosition, size, wxNO_FULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
{
Bind(wxEVT_PAINT, &MetalCanvas::OnPaint, this);
Bind(wxEVT_SIZE, &MetalCanvas::OnResize, this);
auto& canvas = is_main_window ? WindowSystem::GetWindowInfo().canvas_main : WindowSystem::GetWindowInfo().canvas_pad;
canvas = initHandleContextFromWxWidgetsWindow(this);
try
{
if (is_main_window)
g_renderer = std::make_unique<MetalRenderer>();
auto metal_renderer = MetalRenderer::GetInstance();
metal_renderer->InitializeLayer({size.x, size.y}, is_main_window);
}
catch(const std::exception& ex)
{
cemuLog_log(LogType::Force, "Error when initializing Metal renderer: {}", ex.what());
auto msg = formatWxString(_("Error when initializing Metal renderer:\n{}"), ex.what());
wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
dialog.ShowModal();
exit(0);
}
wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES);
}
MetalCanvas::~MetalCanvas()
{
Unbind(wxEVT_PAINT, &MetalCanvas::OnPaint, this);
Unbind(wxEVT_SIZE, &MetalCanvas::OnResize, this);
MetalRenderer* mtlr = (MetalRenderer*)g_renderer.get();
if (mtlr)
mtlr->ShutdownLayer(m_is_main_window);
}
void MetalCanvas::OnPaint(wxPaintEvent& event)
{
}
void MetalCanvas::OnResize(wxSizeEvent& event)
{
const wxSize size = GetSize();
if (size.GetWidth() == 0 || size.GetHeight() == 0)
return;
const wxRect refreshRect(size);
RefreshRect(refreshRect, false);
auto metal_renderer = MetalRenderer::GetInstance();
metal_renderer->ResizeLayer({size.x, size.y}, m_is_main_window);
}

View file

@ -0,0 +1,19 @@
#pragma once
#include "wxgui/canvas/IRenderCanvas.h"
#include <wx/frame.h>
#include <set>
class MetalCanvas : public IRenderCanvas, public wxWindow
{
public:
MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window);
~MetalCanvas();
private:
void OnPaint(wxPaintEvent& event);
void OnResize(wxSizeEvent& event);
};

View file

@ -72,8 +72,11 @@ std::vector<fs::path> _getCachesPaths(const TitleId& titleId)
ActiveSettings::GetCachePath(L"shaderCache/driver/vk/{:016x}.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_spirv.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_gl.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_air.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_shaders.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_vkpipeline.bin", titleId)};
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlshaders.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_vkpipeline.bin", titleId),
ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlpipeline.bin", titleId)};
cachePaths.erase(std::remove_if(cachePaths.begin(), cachePaths.end(),
[](const fs::path& cachePath) {