From d64e0c9b6f688781ef3d48356d34e4e8d895db7c Mon Sep 17 00:00:00 2001 From: Samuliak Date: Sun, 15 Dec 2024 13:18:24 +0100 Subject: [PATCH] rework gpu selection --- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 54 ++++++++++++++++++- .../HW/Latte/Renderer/Metal/MetalRenderer.h | 4 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 14 +++-- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 14 +---- src/config/CemuConfig.cpp | 4 +- src/config/CemuConfig.h | 2 +- src/gui/GeneralSettings2.cpp | 54 ++++++++++++++----- 7 files changed, 105 insertions(+), 41 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index a578948a..50c60523 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -20,6 +20,8 @@ #include "Cemu/Logging/CemuLogging.h" #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/LatteConst.h" +#include "Foundation/NSString.hpp" +#include "Metal/MTLDevice.hpp" #include "config/CemuConfig.h" #include "gui/guiWrapper.h" @@ -36,10 +38,53 @@ 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.push_back(std::string(device->name()->utf8String())); + } + + return result; +} + MetalRenderer::MetalRenderer() { - m_device = MTL::CreateSystemDefaultDevice(); - m_commandQueue = m_device->newCommandQueue(); + // Pick a device + auto& config = GetConfig(); + const bool hasDeviceSet = !config.graphic_device_name.empty(); + + // 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)); + std::string name = std::string(device->name()->utf8String()); + if (name == config.graphic_device_name) + { + 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.graphic_device_name); + config.graphic_device_name = ""; + } + + // Use the system default device + m_device = MTL::CreateSystemDefaultDevice(); + } // Feature support m_isAppleGPU = m_device->supportsFamily(MTL::GPUFamilyApple1); @@ -50,6 +95,9 @@ MetalRenderer::MetalRenderer() CheckForPixelFormatSupport(m_pixelFormatSupport); + // Create command queue + m_commandQueue = m_device->newCommandQueue(); + // Synchronization resources m_event = m_device->newEvent(); @@ -523,6 +571,7 @@ void MetalRenderer::DeleteFontTextures() void MetalRenderer::AppendOverlayDebugInfo() { ImGui::Text("--- GPU info ---"); + ImGui::Text("GPU name %s", m_device->name()->utf8String()); ImGui::Text("Is Apple GPU %s", (m_isAppleGPU ? "yes" : "no")); ImGui::Text("Has unified memory %s", (m_hasUnifiedMemory ? "yes" : "no")); ImGui::Text("Supports Metal3 %s", (m_supportsMetal3 ? "yes" : "no")); @@ -636,6 +685,7 @@ void MetalRenderer::texture_clearColorSlice(LatteTexture* hostTexture, sint32 sl { if (!FormatIsRenderable(hostTexture->format)) { + // TODO: handle this somehow? cemuLog_logOnce(LogType::Force, "cannot clear color texture with format {}, because it's not renderable", hostTexture->format); return; } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index 6a5db69b..cf4fc29f 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -155,6 +155,8 @@ public: static constexpr uint32 OCCLUSION_QUERY_POOL_SIZE = 1024; static constexpr uint32 TEXTURE_READBACK_SIZE = 32 * 1024 * 1024; // 32 MB + static std::vector GetDevices(); + MetalRenderer(); ~MetalRenderer() override; @@ -459,7 +461,7 @@ private: MetalPerformanceMonitor m_performanceMonitor; // Metal objects - MTL::Device* m_device; + MTL::Device* m_device = nullptr; MTL::CommandQueue* m_commandQueue; // Feature support diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 998aac47..600d82de 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -91,7 +91,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback(VkDebugUtilsMessageSeverityFla return VK_FALSE; } -std::vector VulkanRenderer::GetDevices() +std::vector VulkanRenderer::GetDevices() { if(!vkEnumerateInstanceVersion) { @@ -105,7 +105,7 @@ std::vector VulkanRenderer::GetDevices() apiVersion = VK_API_VERSION_1_1; } - std::vector result; + std::vector result; std::vector requiredExtensions; requiredExtensions.clear(); @@ -168,7 +168,7 @@ std::vector VulkanRenderer::GetDevices() physDeviceProps.pNext = &physDeviceIDProps; vkGetPhysicalDeviceProperties2(device, &physDeviceProps); - result.emplace_back(physDeviceProps.properties.deviceName, physDeviceIDProps.deviceUUID); + result.emplace_back(physDeviceProps.properties.deviceName); } } vkDestroySurfaceKHR(instance, surface, nullptr); @@ -181,7 +181,6 @@ std::vector VulkanRenderer::GetDevices() vkDestroyInstance(instance, nullptr); return result; - } void VulkanRenderer::DetermineVendor() @@ -389,8 +388,7 @@ 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; + const bool has_device_set = !config.graphic_device_name.empty(); VkPhysicalDevice fallbackDevice = VK_NULL_HANDLE; @@ -410,7 +408,7 @@ VulkanRenderer::VulkanRenderer() physDeviceProps.pNext = &physDeviceIDProps; vkGetPhysicalDeviceProperties2(device, &physDeviceProps); - if (memcmp(config.graphic_device_uuid.data(), physDeviceIDProps.deviceUUID, VK_UUID_SIZE) != 0) + if (config.graphic_device_name != physDeviceProps.properties.deviceName) continue; } @@ -423,7 +421,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.graphic_device_name = ""; // resetting device selection } else if (m_physicalDevice == VK_NULL_HANDLE) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 867647a3..68309414 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -156,19 +156,7 @@ public: sint32 texelCountY; }FormatInfoVK; - struct DeviceInfo - { - DeviceInfo(const std::string name, uint8* uuid) - : name(name) - { - std::copy(uuid, uuid + VK_UUID_SIZE, this->uuid.data()); - } - - std::string name; - std::array uuid; - }; - - static std::vector GetDevices(); + static std::vector GetDevices(); VulkanRenderer(); virtual ~VulkanRenderer(); diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 0e39656b..701e6be5 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -212,7 +212,7 @@ 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("device", graphic_device_name); vsync = graphic.get("VSync", 0); gx2drawdone_sync = graphic.get("GX2DrawdoneSync", true); upscale_filter = graphic.get("UpscaleFilter", kBicubicHermiteFilter); @@ -468,7 +468,7 @@ void CemuConfig::Save(XMLConfigParser& parser) // graphics auto graphic = config.set("Graphic"); graphic.set("api", graphic_api); - graphic.set("device", graphic_device_uuid); + graphic.set("device", graphic_device_name); 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..0dc8cf2b 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -462,7 +462,7 @@ struct CemuConfig // graphics ConfigValue graphic_api{ kVulkan }; - std::array graphic_device_uuid; + std::string graphic_device_name; ConfigValue vsync{ 0 }; // 0 = off, 1+ = on depending on render backend ConfigValue gx2drawdone_sync {true}; ConfigValue render_upside_down{ false }; diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/GeneralSettings2.cpp index 8b6e0ee1..7071c7e8 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/GeneralSettings2.cpp @@ -1,3 +1,5 @@ +#include "Foundation/NSString.hpp" +#include "Metal/MTLDevice.hpp" #include "gui/wxgui.h" #include "gui/GeneralSettings2.h" #include "gui/CemuApp.h" @@ -27,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 @@ -82,15 +87,15 @@ private: IAudioInputAPI::DeviceDescriptionPtr m_description; }; -class wxVulkanUUID : public wxClientData +class wxGraphicsDevice : public wxClientData { public: - wxVulkanUUID(const VulkanRenderer::DeviceInfo& info) - : m_device_info(info) {} - const VulkanRenderer::DeviceInfo& GetDeviceInfo() const { return m_device_info; } + wxGraphicsDevice(const std::string& name) + : m_name(name) {} + const std::string& GetName() const { return m_name; } private: - VulkanRenderer::DeviceInfo m_device_info; + std::string m_name; }; class wxAccountData : public wxClientData @@ -1025,14 +1030,14 @@ void GeneralSettings2::StoreConfig() selection = m_graphic_device->GetSelection(); if(selection != wxNOT_FOUND) { - const auto* info = (wxVulkanUUID*)m_graphic_device->GetClientObject(selection); + const auto* info = (wxGraphicsDevice*)m_graphic_device->GetClientObject(selection); if(info) - config.graphic_device_uuid = info->GetDeviceInfo().uuid; + config.graphic_device_name = info->GetName(); else - config.graphic_device_uuid = {}; + config.graphic_device_name = ""; } else - config.graphic_device_uuid = {}; + config.graphic_device_name = ""; config.vsync = m_vsync->GetSelection(); @@ -1538,14 +1543,14 @@ void GeneralSettings2::HandleGraphicsApiSelection() { for(const auto& device : devices) { - m_graphic_device->Append(device.name, new wxVulkanUUID(device)); + m_graphic_device->Append(device, new wxGraphicsDevice(device)); } m_graphic_device->SetSelection(0); const auto& config = GetConfig(); for(size_t i = 0; i < devices.size(); ++i) { - if(config.graphic_device_uuid == devices[i].uuid) + if(config.graphic_device_name == devices[i]) { m_graphic_device->SetSelection(i); break; @@ -1566,9 +1571,30 @@ 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(); + m_graphic_device->Clear(); + +#if ENABLE_METAL + auto devices = MetalRenderer::GetDevices(); + if (!devices.empty()) + { + for (const auto& device : devices) + { + m_graphic_device->Append(device, new wxGraphicsDevice(device)); + } + m_graphic_device->SetSelection(0); + + const auto& config = GetConfig(); + for (size_t i = 0; i < devices.size(); ++i) + { + if (config.graphic_device_name == devices[i]) + { + m_graphic_device->SetSelection(i); + break; + } + } + } +#endif } }