summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorder richter <der.richter@gmx.de>2023-09-30 16:01:04 +0200
committerder richter <der.richter@gmx.de>2023-10-14 18:39:56 +0200
commit78d43740f52db817d98bcf24fb30a76ab6fa13ff (patch)
tree4e50dcd3872d7935ca55ca547e8d26502e2c1bfc
parentbc66de2834dcf69d4d05163350a219d2fbc47a56 (diff)
downloadmpv-78d43740f52db817d98bcf24fb30a76ab6fa13ff.tar.bz2
mpv-78d43740f52db817d98bcf24fb30a76ab6fa13ff.tar.xz
vo_gpu/vo_gpu_next: add vulkan support for macOS
add support for vulkan through metal and a translation layer like MoltenVK. also add the possibility to use different render timing modes for testing. i still consider this experimental atm.
-rw-r--r--DOCS/man/options.rst14
-rw-r--r--meson.build15
-rw-r--r--osdep/macOS_swift_bridge.h5
-rw-r--r--osdep/macos/precise_timer.swift153
-rw-r--r--osdep/macosx_application.h7
-rw-r--r--osdep/macosx_application.m3
-rw-r--r--video/out/gpu/context.c4
-rw-r--r--video/out/mac/metal_layer.swift43
-rw-r--r--video/out/mac_common.swift177
-rw-r--r--video/out/vulkan/common.h4
-rw-r--r--video/out/vulkan/context_mac.m119
11 files changed, 538 insertions, 6 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index bee5aa0b10..5b57d4d2c9 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -6273,6 +6273,18 @@ them.
macOS only.
+``--macos-render-timer=<timer>``
+ Sets the mode (default: callback) for syncing the rendering of frames to the display's
+ vertical refresh rate.
+ macOS and Vulkan (macvk) only.
+
+ ``<timer>`` can be one of the following:
+
+ :callback: Syncs to the CVDisplayLink callback
+ :precise: Syncs to the time of the next vertical display refresh reported by the
+ CVDisplayLink callback provided information
+ :system: No manual syncing, depend on the layer mechanic and the next drawable
+
``--android-surface-size=<WxH>``
Set dimensions of the rendering surface used by the Android gpu context.
Needs to be set by the embedding application if the dimensions change during
@@ -6324,6 +6336,8 @@ them.
X11/EGL
android
Android/EGL. Requires ``--wid`` be set to an ``android.view.Surface``.
+ macvk
+ Vulkan on macOS with a metal surface through a translation layer (experimental)
``--gpu-api=<type>``
Controls which type of graphics APIs will be accepted:
diff --git a/meson.build b/meson.build
index 4485c74a56..a56bb1f37c 100644
--- a/meson.build
+++ b/meson.build
@@ -1546,12 +1546,14 @@ swift = get_option('swift-build').require(
darwin and macos_sdk_version.version_compare('>=10.10') and swift_ver.version_compare('>=4.1'),
error_message: 'A suitable macos sdk version or swift version could not be found!',
)
+features += {'swift': swift.allowed()}
swift_sources = []
-if cocoa.found() and swift.allowed()
+if features['cocoa'] and features['swift']
swift_sources += files('osdep/macos/libmpv_helper.swift',
'osdep/macos/log_helper.swift',
'osdep/macos/mpv_helper.swift',
+ 'osdep/macos/precise_timer.swift',
'osdep/macos/swift_compat.swift',
'osdep/macos/swift_extensions.swift',
'video/out/mac/common.swift',
@@ -1561,7 +1563,7 @@ if cocoa.found() and swift.allowed()
endif
macos_cocoa_cb = get_option('macos-cocoa-cb').require(
- features['cocoa'] and swift.allowed(),
+ features['cocoa'] and features['swift'],
error_message: 'Either cocoa or swift could not be found!',
)
features += {'macos-cocoa-cb': macos_cocoa_cb.allowed()}
@@ -1569,9 +1571,14 @@ if features['macos-cocoa-cb']
swift_sources += files('video/out/cocoa_cb_common.swift',
'video/out/mac/gl_layer.swift')
endif
+if features['cocoa'] and features['vulkan'] and features['swift']
+ swift_sources += files('video/out/mac_common.swift',
+ 'video/out/mac/metal_layer.swift')
+ sources += files('video/out/vulkan/context_mac.m')
+endif
macos_media_player = get_option('macos-media-player').require(
- macos_10_12_2_features.allowed() and swift.allowed(),
+ macos_10_12_2_features.allowed() and features['swift'],
error_message: 'Either the macos sdk version is not at least 10.12.2 or swift was not found!',
)
features += {'macos-media-player': macos_media_player.allowed()}
@@ -1579,7 +1586,7 @@ if features['macos-media-player']
swift_sources += files('osdep/macos/remote_command_center.swift')
endif
-if swift.allowed() and swift_sources.length() > 0
+if features['swift'] and swift_sources.length() > 0
subdir('osdep')
endif
diff --git a/osdep/macOS_swift_bridge.h b/osdep/macOS_swift_bridge.h
index 29cd8bf016..9407b6fc9b 100644
--- a/osdep/macOS_swift_bridge.h
+++ b/osdep/macOS_swift_bridge.h
@@ -15,9 +15,10 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-// including IOKit here again doesn't make sense, but otherwise the swift
-// compiler doesn't include the needed header in our generated header file
+// including frameworks here again doesn't make sense, but otherwise the swift
+// compiler doesn't include the needed headers in our generated header file
#import <IOKit/pwr_mgt/IOPMLib.h>
+#import <QuartzCore/QuartzCore.h>
#include "player/client.h"
#include "video/out/libmpv.h"
diff --git a/osdep/macos/precise_timer.swift b/osdep/macos/precise_timer.swift
new file mode 100644
index 0000000000..f4ad3bb6b6
--- /dev/null
+++ b/osdep/macos/precise_timer.swift
@@ -0,0 +1,153 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import Cocoa
+
+struct Timing {
+ let time: UInt64
+ let closure: () -> ()
+}
+
+class PreciseTimer {
+ unowned var common: Common
+ var mpv: MPVHelper? { get { return common.mpv } }
+
+ let nanoPerSecond: Double = 1e+9
+ let machToNano: Double = {
+ var timebase: mach_timebase_info = mach_timebase_info()
+ mach_timebase_info(&timebase)
+ return Double(timebase.numer) / Double(timebase.denom)
+ }()
+
+ let condition = NSCondition()
+ var events: [Timing] = []
+ var isRunning: Bool = true
+ var isHighPrecision: Bool = false
+
+ var thread: pthread_t!
+ var threadPort: thread_port_t = thread_port_t()
+ let policyFlavor = thread_policy_flavor_t(THREAD_TIME_CONSTRAINT_POLICY)
+ let policyCount = MemoryLayout<thread_time_constraint_policy>.size /
+ MemoryLayout<integer_t>.size
+ var typeNumber: mach_msg_type_number_t {
+ return mach_msg_type_number_t(policyCount)
+ }
+ var threadAttr: pthread_attr_t = {
+ var attr = pthread_attr_t()
+ var param = sched_param()
+ pthread_attr_init(&attr)
+ param.sched_priority = sched_get_priority_max(SCHED_FIFO)
+ pthread_attr_setschedparam(&attr, &param)
+ pthread_attr_setschedpolicy(&attr, SCHED_FIFO)
+ return attr
+ }()
+
+ init?(common com: Common) {
+ common = com
+
+ pthread_create(&thread, &threadAttr, entryC, MPVHelper.bridge(obj: self))
+ if thread == nil {
+ common.log.sendWarning("Couldn't create pthread for high precision timer")
+ return nil
+ }
+
+ threadPort = pthread_mach_thread_np(thread)
+ }
+
+ func updatePolicy(periodSeconds: Double = 1 / 60.0) {
+ let period = periodSeconds * nanoPerSecond / machToNano
+ var policy = thread_time_constraint_policy(
+ period: UInt32(period),
+ computation: UInt32(0.75 * period),
+ constraint: UInt32(0.85 * period),
+ preemptible: 1
+ )
+
+ let success = withUnsafeMutablePointer(to: &policy) {
+ $0.withMemoryRebound(to: integer_t.self, capacity: policyCount) {
+ thread_policy_set(threadPort, policyFlavor, $0, typeNumber)
+ }
+ }
+
+ isHighPrecision = success == KERN_SUCCESS
+ if !isHighPrecision {
+ common.log.sendWarning("Couldn't create a high precision timer")
+ }
+ }
+
+ func terminate() {
+ condition.lock()
+ isRunning = false
+ condition.signal()
+ condition.unlock()
+ pthread_kill(thread, SIGALRM)
+ pthread_join(thread, nil)
+ }
+
+ func scheduleAt(time: UInt64, closure: @escaping () -> ()) {
+ condition.lock()
+ let firstEventTime = events.first?.time ?? 0
+ let lastEventTime = events.last?.time ?? 0
+ events.append(Timing(time: time, closure: closure))
+
+ if lastEventTime > time {
+ events.sort{ $0.time < $1.time }
+ }
+
+ condition.signal()
+ condition.unlock()
+
+ if firstEventTime > time {
+ pthread_kill(thread, SIGALRM)
+ }
+ }
+
+ let threadSignal: @convention(c) (Int32) -> () = { (sig: Int32) in }
+
+ let entryC: @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? = { (ptr: UnsafeMutableRawPointer) in
+ let ptimer: PreciseTimer = MPVHelper.bridge(ptr: ptr)
+ ptimer.entry()
+ return nil
+ }
+
+ func entry() {
+ signal(SIGALRM, threadSignal)
+
+ while isRunning {
+ condition.lock()
+ while events.count == 0 && isRunning {
+ condition.wait()
+ }
+
+ if !isRunning { break }
+
+ guard let event = events.first else {
+ continue
+ }
+ condition.unlock()
+
+ mach_wait_until(event.time)
+
+ condition.lock()
+ if events.first?.time == event.time && isRunning {
+ event.closure()
+ events.removeFirst()
+ }
+ condition.unlock()
+ }
+ }
+}
diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h
index d95940fd31..753b9f033f 100644
--- a/osdep/macosx_application.h
+++ b/osdep/macosx_application.h
@@ -26,6 +26,12 @@ enum {
FRAME_WHOLE,
};
+enum {
+ RENDER_TIMER_CALLBACK = 0,
+ RENDER_TIMER_PRECISE,
+ RENDER_TIMER_SYSTEM,
+};
+
struct macos_opts {
int macos_title_bar_style;
int macos_title_bar_appearance;
@@ -35,6 +41,7 @@ struct macos_opts {
bool macos_force_dedicated_gpu;
int macos_app_activation_policy;
int macos_geometry_calculation;
+ int macos_render_timer;
int cocoa_cb_sw_renderer;
bool cocoa_cb_10bit_context;
};
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index fc1da01d39..bf60e05389 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -67,6 +67,9 @@ const struct m_sub_options macos_conf = {
{"regular", 0}, {"accessory", 1}, {"prohibited", 2})},
{"macos-geometry-calculation", OPT_CHOICE(macos_geometry_calculation,
{"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
+ {"macos-render-timer", OPT_CHOICE(macos_render_timer,
+ {"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
+ {"system", RENDER_TIMER_SYSTEM})},
{"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
{"auto", -1}, {"no", 0}, {"yes", 1})},
{"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},
diff --git a/video/out/gpu/context.c b/video/out/gpu/context.c
index 5b4987eeef..28f88014aa 100644
--- a/video/out/gpu/context.c
+++ b/video/out/gpu/context.c
@@ -51,6 +51,7 @@ extern const struct ra_ctx_fns ra_ctx_vulkan_win;
extern const struct ra_ctx_fns ra_ctx_vulkan_xlib;
extern const struct ra_ctx_fns ra_ctx_vulkan_android;
extern const struct ra_ctx_fns ra_ctx_vulkan_display;
+extern const struct ra_ctx_fns ra_ctx_vulkan_mac;
/* Direct3D 11 */
extern const struct ra_ctx_fns ra_ctx_d3d11;
@@ -113,6 +114,9 @@ static const struct ra_ctx_fns *contexts[] = {
#if HAVE_VK_KHR_DISPLAY
&ra_ctx_vulkan_display,
#endif
+#if HAVE_COCOA && HAVE_SWIFT
+ &ra_ctx_vulkan_mac,
+#endif
#endif
/* No API contexts: */
diff --git a/video/out/mac/metal_layer.swift b/video/out/mac/metal_layer.swift
new file mode 100644
index 0000000000..7cea87c0b4
--- /dev/null
+++ b/video/out/mac/metal_layer.swift
@@ -0,0 +1,43 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import Cocoa
+
+class MetalLayer: CAMetalLayer {
+ unowned var common: MacCommon
+
+ init(common com: MacCommon) {
+ common = com
+ super.init()
+
+ pixelFormat = .rgba16Float
+ backgroundColor = NSColor.black.cgColor
+ }
+
+ // necessary for when the layer containing window changes the screen
+ override init(layer: Any) {
+ guard let oldLayer = layer as? MetalLayer else {
+ fatalError("init(layer: Any) passed an invalid layer")
+ }
+ common = oldLayer.common
+ super.init()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+}
diff --git a/video/out/mac_common.swift b/video/out/mac_common.swift
new file mode 100644
index 0000000000..12d2870add
--- /dev/null
+++ b/video/out/mac_common.swift
@@ -0,0 +1,177 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import Cocoa
+
+class MacCommon: Common {
+ @objc var layer: MetalLayer?
+
+ var timer: PreciseTimer?
+ var swapTime: UInt64 = 0
+ let swapLock: NSCondition = NSCondition()
+
+ var needsICCUpdate: Bool = false
+
+ @objc init(_ vo: UnsafeMutablePointer<vo>) {
+ let newlog = mp_log_new(vo, vo.pointee.log, "mac")
+ super.init(newlog)
+ mpv = MPVHelper(vo, log)
+ timer = PreciseTimer(common: self)
+
+ DispatchQueue.main.sync {
+ layer = MetalLayer(common: self)
+ initMisc(vo)
+ }
+ }
+
+ @objc func config(_ vo: UnsafeMutablePointer<vo>) -> Bool {
+ mpv?.vo = vo
+
+ DispatchQueue.main.sync {
+ let previousActiveApp = getActiveApp()
+ initApp()
+
+ let (_, _, wr) = getInitProperties(vo)
+
+ guard let layer = self.layer else {
+ log.sendError("Something went wrong, no MetalLayer was initialized")
+ exit(1)
+ }
+
+ if window == nil {
+ initView(vo, layer)
+ initWindow(vo, previousActiveApp)
+ initWindowState()
+ }
+
+ if !NSEqualSizes(window?.unfsContentFramePixel.size ?? NSZeroSize, wr.size) {
+ window?.updateSize(wr.size)
+ }
+
+ windowDidResize()
+ needsICCUpdate = true
+ }
+
+ return true
+ }
+
+ @objc func uninit(_ vo: UnsafeMutablePointer<vo>) {
+ window?.waitForAnimation()
+
+ timer?.terminate()
+
+ DispatchQueue.main.sync {
+ window?.delegate = nil
+ window?.close()
+
+ uninitCommon()
+ }
+ }
+
+ @objc func swapBuffer() {
+ if mpv?.macOpts.macos_render_timer ?? Int32(RENDER_TIMER_CALLBACK) != RENDER_TIMER_SYSTEM {
+ swapLock.lock()
+ while(swapTime < 1) {
+ swapLock.wait()
+ }
+ swapTime = 0
+ swapLock.unlock()
+ }
+
+ if needsICCUpdate {
+ needsICCUpdate = false
+ updateICCProfile()
+ }
+ }
+
+ func updateRenderSize(_ size: NSSize) {
+ mpv?.vo.pointee.dwidth = Int32(size.width)
+ mpv?.vo.pointee.dheight = Int32(size.height)
+ flagEvents(VO_EVENT_RESIZE | VO_EVENT_EXPOSE)
+ }
+
+ override func displayLinkCallback(_ displayLink: CVDisplayLink,
+ _ inNow: UnsafePointer<CVTimeStamp>,
+ _ inOutputTime: UnsafePointer<CVTimeStamp>,
+ _ flagsIn: CVOptionFlags,
+ _ flagsOut: UnsafeMutablePointer<CVOptionFlags>) -> CVReturn
+ {
+ let frameTimer = mpv?.macOpts.macos_render_timer ?? Int32(RENDER_TIMER_CALLBACK)
+ let signalSwap = { [self] in
+ swapLock.lock()
+ swapTime += 1
+ swapLock.signal()
+ swapLock.unlock()
+ }
+
+ if frameTimer != RENDER_TIMER_SYSTEM {
+ if let timer = self.timer, frameTimer == RENDER_TIMER_PRECISE {
+ timer.scheduleAt(time: inOutputTime.pointee.hostTime, closure: signalSwap)
+ return kCVReturnSuccess
+ }
+
+ signalSwap()
+ }
+
+ return kCVReturnSuccess
+ }
+
+ override func startDisplayLink(_ vo: UnsafeMutablePointer<vo>) {
+ super.startDisplayLink(vo)
+ timer?.updatePolicy(periodSeconds: 1 / currentFps())
+ }
+
+ override func updateDisplaylink() {
+ super.updateDisplaylink()
+ timer?.updatePolicy(periodSeconds: 1 / currentFps())
+ }
+
+ override func lightSensorUpdate() {
+ flagEvents(VO_EVENT_AMBIENT_LIGHTING_CHANGED)
+ }
+
+ @objc override func updateICCProfile() {
+ guard let colorSpace = window?.screen?.colorSpace else {
+ log.sendWarning("Couldn't update ICC Profile, no color space available")
+ return
+ }
+
+ if #available(macOS 10.11, *) {
+ layer?.colorspace = colorSpace.cgColorSpace
+ }
+
+ flagEvents(VO_EVENT_ICC_PROFILE_CHANGED)
+ }
+
+ override func windowDidResize() {
+ guard let window = window else {
+ log.sendWarning("No window available on window resize event")
+ return
+ }
+
+ updateRenderSize(window.framePixel.size)
+ }
+
+ override func windowDidChangeScreenProfile() {
+ needsICCUpdate = true
+ }
+
+ override func windowDidChangeBackingProperties() {
+ layer?.contentsScale = window?.backingScaleFactor ?? 1
+ windowDidResize()
+ }
+}
diff --git a/video/out/vulkan/common.h b/video/out/vulkan/common.h
index 85e6c50f4b..d006942d41 100644
--- a/video/out/vulkan/common.h
+++ b/video/out/vulkan/common.h
@@ -22,6 +22,10 @@
#if HAVE_WIN32_DESKTOP
#define VK_USE_PLATFORM_WIN32_KHR
#endif
+#if HAVE_COCOA
+#define VK_USE_PLATFORM_MACOS_MVK
+#define VK_USE_PLATFORM_METAL_EXT
+#endif
#include <libplacebo/vulkan.h>
diff --git a/video/out/vulkan/context_mac.m b/video/out/vulkan/context_mac.m
new file mode 100644
index 0000000000..8ac6e169f6
--- /dev/null
+++ b/video/out/vulkan/context_mac.m
@@ -0,0 +1,119 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "video/out/gpu/context.h"
+#include "osdep/macOS_swift.h"
+
+#include "common.h"
+#include "context.h"
+#include "utils.h"
+
+struct priv {
+ struct mpvk_ctx vk;
+ MacCommon *vo_mac;
+};
+
+static void mac_vk_uninit(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+
+ ra_vk_ctx_uninit(ctx);
+ mpvk_uninit(&p->vk);
+ [p->vo_mac uninit:ctx->vo];
+}
+
+static void mac_vk_swap_buffers(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ [p->vo_mac swapBuffer];
+}
+
+static bool mac_vk_init(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
+ struct mpvk_ctx *vk = &p->vk;
+ int msgl = ctx->opts.probing ? MSGL_V : MSGL_ERR;
+
+ if (!mpvk_init(vk, ctx, VK_EXT_METAL_SURFACE_EXTENSION_NAME))
+ goto error;
+
+ p->vo_mac = [[MacCommon alloc] init:ctx->vo];
+ if (!p->vo_mac)
+ goto error;
+
+ VkMetalSurfaceCreateInfoEXT mac_info = {
+ .sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
+ .pNext = NULL,
+ .flags = 0,
+ .pLayer = p->vo_mac.layer,
+ };
+
+ struct ra_vk_ctx_params params = {
+ .swap_buffers = mac_vk_swap_buffers,
+ };
+
+ VkInstance inst = vk->vkinst->instance;
+ VkResult res = vkCreateMetalSurfaceEXT(inst, &mac_info, NULL, &vk->surface);
+ if (res != VK_SUCCESS) {
+ MP_MSG(ctx, msgl, "Failed creating metal surface\n");
+ goto error;
+ }
+
+ if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_FIFO_KHR))
+ goto error;
+
+ return true;
+error:
+ if (p->vo_mac)
+ [p->vo_mac uninit:ctx->vo];
+ return false;
+}
+
+static bool resize(struct ra_ctx *ctx)
+{
+ return ra_vk_ctx_resize(ctx, ctx->vo->dwidth, ctx->vo->dheight);
+}
+
+static bool mac_vk_reconfig(struct ra_ctx *ctx)
+{
+ struct priv *p = ctx->priv;
+ if (![p->vo_mac config:ctx->vo])
+ return false;
+ return true;
+}
+
+static int mac_vk_control(struct ra_ctx *ctx, int *events, int request, void *arg)
+{
+ struct priv *p = ctx->priv;
+ int ret = [p->vo_mac control:ctx->vo events:events request:request data:arg];
+
+ if (*events & VO_EVENT_RESIZE) {
+ if (!resize(ctx))
+ return VO_ERROR;
+ }
+
+ return ret;
+}
+
+const struct ra_ctx_fns ra_ctx_vulkan_mac = {
+ .type = "vulkan",
+ .name = "macvk",
+ .reconfig = mac_vk_reconfig,
+ .control = mac_vk_control,
+ .init = mac_vk_init,
+ .uninit = mac_vk_uninit,
+};