// Copyright 2022 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "VideoBackends/Metal/VideoBackend.h" // This must be included before we use any TARGET_OS_* macros. #include #if TARGET_OS_OSX #include #endif #include #include #include "Common/Common.h" #include "Common/MsgHandler.h" #include "VideoBackends/Metal/MTLBoundingBox.h" #include "VideoBackends/Metal/MTLGfx.h" #include "VideoBackends/Metal/MTLObjectCache.h" #include "VideoBackends/Metal/MTLPerfQuery.h" #include "VideoBackends/Metal/MTLStateTracker.h" #include "VideoBackends/Metal/MTLUtil.h" #include "VideoBackends/Metal/MTLVertexManager.h" #include "VideoCommon/AbstractGfx.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" std::string Metal::VideoBackend::GetName() const { return NAME; } std::string Metal::VideoBackend::GetDisplayName() const { // i18n: Apple's Metal graphics API (https://developer.apple.com/metal/) return _trans("Metal"); } std::optional Metal::VideoBackend::GetWarningMessage() const { if (Util::GetAdapterList().empty()) { return _trans("No Metal-compatible GPUs were found. " "Use the OpenGL backend or upgrade your computer/GPU"); } return std::nullopt; } static bool WindowSystemTypeSupportsMetal(WindowSystemType type) { switch (type) { case WindowSystemType::MacOS: case WindowSystemType::Headless: return true; default: return false; } } bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi) { @autoreleasepool { const bool surface_ok = wsi.type == WindowSystemType::Headless || wsi.render_surface; if (!WindowSystemTypeSupportsMetal(wsi.type) || !surface_ok) { PanicAlertFmt("Bad WindowSystemInfo for Metal renderer."); return false; } auto devs = Util::GetAdapterList(); if (devs.empty()) { PanicAlertFmt("No Metal GPUs detected."); return false; } Util::PopulateBackendInfo(&g_Config); Util::PopulateBackendInfoAdapters(&g_Config, devs); // Since we haven't called InitializeShared yet, iAdapter may be out of range, // so we have to check it ourselves. size_t selected_adapter_index = static_cast(g_Config.iAdapter); if (selected_adapter_index >= devs.size()) { WARN_LOG_FMT(VIDEO, "Metal adapter index out of range, selecting default adapter."); selected_adapter_index = 0; } MRCOwned> adapter = std::move(devs[selected_adapter_index]); Util::PopulateBackendInfoFeatures(&g_Config, adapter); #if TARGET_OS_OSX if (@available(macOS 13.3, *)) [adapter setShouldMaximizeConcurrentCompilation:YES]; #endif UpdateActiveConfig(); MRCOwned layer = MRCRetain(static_cast(wsi.render_surface)); [layer setDevice:adapter]; if (Util::ToAbstract([layer pixelFormat]) == AbstractTextureFormat::Undefined) [layer setPixelFormat:MTLPixelFormatBGRA8Unorm]; ObjectCache::Initialize(std::move(adapter)); g_state_tracker = std::make_unique(); return InitializeShared( std::make_unique(std::move(layer)), std::make_unique(), std::make_unique(), std::make_unique()); } } void Metal::VideoBackend::Shutdown() { ShutdownShared(); g_state_tracker.reset(); ObjectCache::Shutdown(); } void Metal::VideoBackend::InitBackendInfo(const WindowSystemInfo& wsi) { @autoreleasepool { Util::PopulateBackendInfo(&g_Config); auto adapters = Util::GetAdapterList(); Util::PopulateBackendInfoAdapters(&g_Config, adapters); if (!adapters.empty()) { // Use the selected adapter, or the first to fill features. size_t index = static_cast(g_Config.iAdapter); if (index >= adapters.size()) index = 0; Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]); } } } void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi) { #if TARGET_OS_OSX if (wsi.type != WindowSystemType::MacOS) return; NSView* view = static_cast(wsi.render_surface); CAMetalLayer* layer = [CAMetalLayer layer]; [view setWantsLayer:YES]; [view setLayer:layer]; wsi.render_surface = layer; #endif }