From 08ea28f56ec679d87154c7ef141ff8a592b3dbbd Mon Sep 17 00:00:00 2001 From: Samuliak Date: Wed, 18 Dec 2024 19:51:58 +0100 Subject: [PATCH] add an option to select gpu for metal --- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 48 +++++++++++- .../HW/Latte/Renderer/Metal/MetalRenderer.h | 8 ++ .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 8 +- src/config/CemuConfig.cpp | 6 +- src/config/CemuConfig.h | 5 +- src/gui/GeneralSettings2.cpp | 76 ++++++++++++++++--- 6 files changed, 129 insertions(+), 22 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 86ce0956..ee2fff89 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -36,10 +36,51 @@ float supportBufferData[512 * 4]; // Defined in the OpenGL renderer void LatteDraw_handleSpecialState8_clearAsDepth(); +std::vector MetalRenderer::GetDevices() +{ + auto devices = MTL::CopyAllDevices(); + std::vector result; + result.reserve(devices->count()); + for (uint32 i = 0; i < devices->count(); i++) + { + MTL::Device* device = static_cast(devices->object(i)); + result.emplace_back(std::string(device->name()->utf8String()), device->registryID()); + } + + return result; +} + MetalRenderer::MetalRenderer() { - m_device = MTL::CreateSystemDefaultDevice(); - m_commandQueue = m_device->newCommandQueue(); + // Pick a device + auto& config = GetConfig(); + const bool hasDeviceSet = config.mtl_graphic_device_uuid != 0; + + // If a device is set, try to find it + if (hasDeviceSet) + { + auto devices = MTL::CopyAllDevices(); + for (uint32 i = 0; i < devices->count(); i++) + { + MTL::Device* device = static_cast(devices->object(i)); + if (device->registryID() == config.mtl_graphic_device_uuid) + { + m_device = device; + break; + } + } + } + + if (!m_device) + { + if (hasDeviceSet) + { + cemuLog_log(LogType::Force, "The selected GPU ({}) could not be found. Using the system default device.", config.mtl_graphic_device_uuid); + config.mtl_graphic_device_uuid = 0; + } + // Use the system default device + m_device = MTL::CreateSystemDefaultDevice(); + } // Feature support m_isAppleGPU = m_device->supportsFamily(MTL::GPUFamilyApple1); @@ -50,6 +91,9 @@ MetalRenderer::MetalRenderer() CheckForPixelFormatSupport(m_pixelFormatSupport); + // Command queue + m_commandQueue = m_device->newCommandQueue(); + // Synchronization resources m_event = m_device->newEvent(); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index 6a5db69b..1deddd04 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -155,6 +155,14 @@ public: static constexpr uint32 OCCLUSION_QUERY_POOL_SIZE = 1024; static constexpr uint32 TEXTURE_READBACK_SIZE = 32 * 1024 * 1024; // 32 MB + struct DeviceInfo + { + std::string name; + uint64 uuid; + }; + + static std::vector GetDevices(); + MetalRenderer(); ~MetalRenderer() override; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index a28eef4e..98959b2c 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -389,8 +389,8 @@ VulkanRenderer::VulkanRenderer() auto surface = CreateFramebufferSurface(m_instance, gui_getWindowInfo().window_main); auto& config = GetConfig(); - decltype(config.graphic_device_uuid) zero{}; - const bool has_device_set = config.graphic_device_uuid != zero; + decltype(config.vk_graphic_device_uuid) zero{}; + const bool has_device_set = config.vk_graphic_device_uuid != zero; VkPhysicalDevice fallbackDevice = VK_NULL_HANDLE; @@ -410,7 +410,7 @@ VulkanRenderer::VulkanRenderer() physDeviceProps.pNext = &physDeviceIDProps; vkGetPhysicalDeviceProperties2(device, &physDeviceProps); - if (memcmp(config.graphic_device_uuid.data(), physDeviceIDProps.deviceUUID, VK_UUID_SIZE) != 0) + if (memcmp(config.vk_graphic_device_uuid.data(), physDeviceIDProps.deviceUUID, VK_UUID_SIZE) != 0) continue; } @@ -423,7 +423,7 @@ VulkanRenderer::VulkanRenderer() { cemuLog_log(LogType::Force, "The selected GPU could not be found or is not suitable. Falling back to first available device instead"); m_physicalDevice = fallbackDevice; - config.graphic_device_uuid = {}; // resetting device selection + config.vk_graphic_device_uuid = {}; // resetting device selection } else if (m_physicalDevice == VK_NULL_HANDLE) { diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 0e39656b..00e56d6d 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -212,7 +212,8 @@ void CemuConfig::Load(XMLConfigParser& parser) // graphics auto graphic = parser.get("Graphic"); graphic_api = graphic.get("api", kOpenGL); - graphic.get("device", graphic_device_uuid); + graphic.get("vkDevice", vk_graphic_device_uuid); + graphic.get("mtlDevice", mtl_graphic_device_uuid); vsync = graphic.get("VSync", 0); gx2drawdone_sync = graphic.get("GX2DrawdoneSync", true); upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter); @@ -468,7 +469,8 @@ void CemuConfig::Save(XMLConfigParser& parser) // graphics auto graphic = config.set("Graphic"); graphic.set("api", graphic_api); - graphic.set("device", graphic_device_uuid); + graphic.set("vkDevice", vk_graphic_device_uuid); + graphic.set("mtlDevice", mtl_graphic_device_uuid); graphic.set("VSync", vsync); graphic.set("GX2DrawdoneSync", gx2drawdone_sync); //graphic.set("PrecompiledShaders", precompiled_shaders.GetValue()); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index d1e5e214..b3ff1999 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -462,8 +462,9 @@ struct CemuConfig // graphics ConfigValue graphic_api{ kVulkan }; - std::array graphic_device_uuid; - ConfigValue vsync{ 0 }; // 0 = off, 1+ = on depending on render backend + std::array vk_graphic_device_uuid; + uint64 mtl_graphic_device_uuid{0}; + ConfigValue vsync{ 0 }; // 0 = off, 1+ = depending on render backend ConfigValue gx2drawdone_sync {true}; ConfigValue render_upside_down{ false }; ConfigValue async_compile{ true }; diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 8b6e0ee1..8663dbff 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -27,6 +27,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 @@ -93,6 +96,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: @@ -1023,16 +1039,32 @@ 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 if (config.graphic_api == GraphicAPI::kMetal) + { + if (selection != wxNOT_FOUND) + { + 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 = {}; + } + else + config.mtl_graphic_device_uuid = {}; } - else - config.graphic_device_uuid = {}; config.vsync = m_vsync->GetSelection(); @@ -1545,7 +1577,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; @@ -1566,9 +1598,29 @@ void GeneralSettings2::HandleGraphicsApiSelection() m_vsync->Select(selection); - // TODO: add an option to select the graphic device - m_graphic_device->Clear(); - m_graphic_device->Disable(); + m_graphic_device->Enable(); + auto devices = MetalRenderer::GetDevices(); + m_graphic_device->Clear(); +#if ENABLE_METAL + 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 } }