2022-06-01 09:58:13 +00:00
|
|
|
|
// Copyright 2022 Dolphin Emulator Project
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
|
|
#include "VideoBackends/Metal/VideoBackend.h"
|
|
|
|
|
|
2022-07-23 18:22:44 +00:00
|
|
|
|
// This must be included before we use any TARGET_OS_* macros.
|
|
|
|
|
#include <TargetConditionals.h>
|
|
|
|
|
|
2022-07-23 08:30:48 +00:00
|
|
|
|
#if TARGET_OS_OSX
|
2022-06-01 09:58:13 +00:00
|
|
|
|
#include <AppKit/AppKit.h>
|
2022-07-23 08:30:48 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2022-06-01 09:58:13 +00:00
|
|
|
|
#include <Metal/Metal.h>
|
|
|
|
|
#include <QuartzCore/QuartzCore.h>
|
|
|
|
|
|
|
|
|
|
#include "Common/Common.h"
|
|
|
|
|
#include "Common/MsgHandler.h"
|
|
|
|
|
|
2023-01-29 01:55:53 +00:00
|
|
|
|
#include "VideoBackends/Metal/MTLBoundingBox.h"
|
2023-01-31 06:21:15 +00:00
|
|
|
|
#include "VideoBackends/Metal/MTLGfx.h"
|
2022-06-01 09:58:13 +00:00
|
|
|
|
#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"
|
|
|
|
|
|
2023-01-29 01:55:53 +00:00
|
|
|
|
#include "VideoCommon/AbstractGfx.h"
|
2022-06-01 09:58:13 +00:00
|
|
|
|
#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<std::string> 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:
|
2022-11-03 04:06:36 +00:00
|
|
|
|
case WindowSystemType::Headless:
|
2022-06-01 09:58:13 +00:00
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Metal::VideoBackend::Initialize(const WindowSystemInfo& wsi)
|
|
|
|
|
{
|
|
|
|
|
@autoreleasepool
|
|
|
|
|
{
|
2022-11-03 04:06:36 +00:00
|
|
|
|
const bool surface_ok = wsi.type == WindowSystemType::Headless || wsi.render_surface;
|
|
|
|
|
if (!WindowSystemTypeSupportsMetal(wsi.type) || !surface_ok)
|
2022-06-01 09:58:13 +00:00
|
|
|
|
{
|
|
|
|
|
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<size_t>(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<id<MTLDevice>> adapter = std::move(devs[selected_adapter_index]);
|
|
|
|
|
Util::PopulateBackendInfoFeatures(&g_Config, adapter);
|
|
|
|
|
|
2023-06-08 23:57:41 +00:00
|
|
|
|
#if TARGET_OS_OSX
|
2023-11-22 21:59:35 +00:00
|
|
|
|
// This should be available on all macOS 13.3+ systems – but when using OCLP drivers, some devices
|
|
|
|
|
// fail with "Unrecognized selector -[MTLIGAccelDevice setShouldMaximizeConcurrentCompilation:]"
|
|
|
|
|
//
|
|
|
|
|
// This concerns Intel Ivy Bridge, Haswell and Nvidia Kepler on macOS 13.3 or newer.
|
|
|
|
|
// (See
|
|
|
|
|
// https://github.com/dortania/OpenCore-Legacy-Patcher/blob/34676702f494a2a789c514cc76dba19b8b7206b1/docs/PATCHEXPLAIN.md?plain=1#L354C1-L354C83)
|
|
|
|
|
//
|
|
|
|
|
// Perform the feature detection dynamically instead.
|
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
|
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
|
|
|
|
|
|
|
|
|
if ([adapter respondsToSelector:@selector(setShouldMaximizeConcurrentCompilation:)])
|
|
|
|
|
{
|
2023-06-08 23:57:41 +00:00
|
|
|
|
[adapter setShouldMaximizeConcurrentCompilation:YES];
|
2023-11-22 21:59:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma clang diagnostic pop
|
2023-06-08 23:57:41 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-01-29 01:55:53 +00:00
|
|
|
|
UpdateActiveConfig();
|
2022-06-01 09:58:13 +00:00
|
|
|
|
|
|
|
|
|
MRCOwned<CAMetalLayer*> layer = MRCRetain(static_cast<CAMetalLayer*>(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<StateTracker>();
|
|
|
|
|
|
2023-01-29 01:55:53 +00:00
|
|
|
|
return InitializeShared(
|
2023-01-31 06:21:15 +00:00
|
|
|
|
std::make_unique<Metal::Gfx>(std::move(layer)), std::make_unique<Metal::VertexManager>(),
|
|
|
|
|
std::make_unique<Metal::PerfQuery>(), std::make_unique<Metal::BoundingBox>());
|
2022-06-01 09:58:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Metal::VideoBackend::Shutdown()
|
|
|
|
|
{
|
|
|
|
|
ShutdownShared();
|
2023-01-29 01:55:53 +00:00
|
|
|
|
|
|
|
|
|
g_state_tracker.reset();
|
|
|
|
|
ObjectCache::Shutdown();
|
2022-06-01 09:58:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-26 00:16:53 +00:00
|
|
|
|
void Metal::VideoBackend::InitBackendInfo(const WindowSystemInfo& wsi)
|
2022-06-01 09:58:13 +00:00
|
|
|
|
{
|
|
|
|
|
@autoreleasepool
|
|
|
|
|
{
|
|
|
|
|
Util::PopulateBackendInfo(&g_Config);
|
2022-06-21 07:07:35 +00:00
|
|
|
|
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<size_t>(g_Config.iAdapter);
|
|
|
|
|
if (index >= adapters.size())
|
|
|
|
|
index = 0;
|
|
|
|
|
Util::PopulateBackendInfoFeatures(&g_Config, adapters[index]);
|
|
|
|
|
}
|
2022-06-01 09:58:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Metal::VideoBackend::PrepareWindow(WindowSystemInfo& wsi)
|
|
|
|
|
{
|
2022-07-23 08:30:48 +00:00
|
|
|
|
#if TARGET_OS_OSX
|
2022-06-01 09:58:13 +00:00
|
|
|
|
if (wsi.type != WindowSystemType::MacOS)
|
|
|
|
|
return;
|
|
|
|
|
NSView* view = static_cast<NSView*>(wsi.render_surface);
|
|
|
|
|
CAMetalLayer* layer = [CAMetalLayer layer];
|
2024-03-10 07:25:33 +00:00
|
|
|
|
|
|
|
|
|
Util::PopulateBackendInfo(&g_Config);
|
|
|
|
|
|
|
|
|
|
if (g_Config.backend_info.bSupportsHDROutput && g_Config.bHDR)
|
|
|
|
|
{
|
|
|
|
|
[layer setWantsExtendedDynamicRangeContent:YES];
|
|
|
|
|
[layer setPixelFormat:MTLPixelFormatRGBA16Float];
|
|
|
|
|
|
|
|
|
|
const CFStringRef name = kCGColorSpaceExtendedLinearSRGB;
|
|
|
|
|
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name);
|
|
|
|
|
[layer setColorspace:colorspace];
|
|
|
|
|
CGColorSpaceRelease(colorspace);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-01 09:58:13 +00:00
|
|
|
|
[view setWantsLayer:YES];
|
|
|
|
|
[view setLayer:layer];
|
2024-03-10 07:25:33 +00:00
|
|
|
|
|
2022-06-01 09:58:13 +00:00
|
|
|
|
wsi.render_surface = layer;
|
2022-07-23 08:30:48 +00:00
|
|
|
|
#endif
|
2022-06-01 09:58:13 +00:00
|
|
|
|
}
|