Skip to content

Commit b6ce1d8

Browse files
committed
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.
1 parent c0ab0bf commit b6ce1d8

File tree

11 files changed

+534
-2
lines changed

11 files changed

+534
-2
lines changed

DOCS/man/options.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6275,6 +6275,18 @@ them.
62756275

62766276
macOS only.
62776277

6278+
``--macos-render-timer=<timer>``
6279+
Sets the mode (default: callback) for syncing the rendering of frames to the display's
6280+
vertical refresh rate.
6281+
macOS and Vulkan (macvk) only.
6282+
6283+
``<timer>`` can be one of the following:
6284+
6285+
:callback: Syncs to the CVDisplayLink callback
6286+
:precise: Syncs to the time of the next vertical display refresh reported by the
6287+
CVDisplayLink callback provided information
6288+
:system: No manual syncing, depend on the layer mechanic and the next drawable
6289+
62786290
``--android-surface-size=<WxH>``
62796291
Set dimensions of the rendering surface used by the Android gpu context.
62806292
Needs to be set by the embedding application if the dimensions change during
@@ -6326,6 +6338,8 @@ them.
63266338
X11/EGL
63276339
android
63286340
Android/EGL. Requires ``--wid`` be set to an ``android.view.Surface``.
6341+
macvk
6342+
Vulkan on macOS with a metal surface through a translation layer (experimental)
63296343

63306344
``--gpu-api=<type>``
63316345
Controls which type of graphics APIs will be accepted:

meson.build

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,12 +1543,14 @@ swift = get_option('swift-build').require(
15431543
darwin and macos_sdk_version.version_compare('>=10.10') and swift_ver.version_compare('>=4.1'),
15441544
error_message: 'A suitable macos sdk version or swift version could not be found!',
15451545
)
1546+
features += {'swift': swift.allowed()}
15461547

15471548
swift_sources = []
15481549
if cocoa.found() and swift.allowed()
15491550
swift_sources += files('osdep/macos/libmpv_helper.swift',
15501551
'osdep/macos/log_helper.swift',
15511552
'osdep/macos/mpv_helper.swift',
1553+
'osdep/macos/precise_timer.swift',
15521554
'osdep/macos/swift_compat.swift',
15531555
'osdep/macos/swift_extensions.swift',
15541556
'video/out/mac/common.swift',
@@ -1566,6 +1568,11 @@ if features['macos-cocoa-cb']
15661568
swift_sources += files('video/out/cocoa_cb_common.swift',
15671569
'video/out/mac/gl_layer.swift')
15681570
endif
1571+
if features['cocoa'] and features['vulkan'] and swift.allowed()
1572+
swift_sources += files('video/out/mac_common.swift',
1573+
'video/out/mac/metal_layer.swift')
1574+
sources += files('video/out/vulkan/context_mac.m')
1575+
endif
15691576

15701577
macos_media_player = get_option('macos-media-player').require(
15711578
macos_10_12_2_features.allowed() and swift.allowed(),

osdep/macOS_swift_bridge.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
// including IOKit here again doesn't make sense, but otherwise the swift
19-
// compiler doesn't include the needed header in our generated header file
18+
// including frameworks here again doesn't make sense, but otherwise the swift
19+
// compiler doesn't include the needed headers in our generated header file
2020
#import <IOKit/pwr_mgt/IOPMLib.h>
21+
#import <QuartzCore/QuartzCore.h>
2122

2223
#include "player/client.h"
2324
#include "video/out/libmpv.h"

osdep/macos/precise_timer.swift

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* This file is part of mpv.
3+
*
4+
* mpv is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* mpv is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import Cocoa
19+
20+
struct Timing {
21+
let time: UInt64
22+
let closure: () -> ()
23+
}
24+
25+
class PreciseTimer {
26+
unowned var common: Common
27+
var mpv: MPVHelper? { get { return common.mpv } }
28+
29+
let nanoPerSecond: Double = 1e+9
30+
let machToNano: Double = {
31+
var timebase: mach_timebase_info = mach_timebase_info()
32+
mach_timebase_info(&timebase)
33+
return Double(timebase.numer) / Double(timebase.denom)
34+
}()
35+
36+
let condition = NSCondition()
37+
var events: [Timing] = []
38+
var isRunning: Bool = true
39+
var isHighPrecision: Bool = false
40+
41+
var thread: pthread_t!
42+
var threadPort: thread_port_t = thread_port_t()
43+
let policyFlavor = thread_policy_flavor_t(THREAD_TIME_CONSTRAINT_POLICY)
44+
let policyCount = MemoryLayout<thread_time_constraint_policy>.size /
45+
MemoryLayout<integer_t>.size
46+
var typeNumber: mach_msg_type_number_t {
47+
return mach_msg_type_number_t(policyCount)
48+
}
49+
var threadAttr: pthread_attr_t = {
50+
var attr = pthread_attr_t()
51+
var param = sched_param()
52+
pthread_attr_init(&attr)
53+
param.sched_priority = sched_get_priority_max(SCHED_FIFO)
54+
pthread_attr_setschedparam(&attr, &param)
55+
pthread_attr_setschedpolicy(&attr, SCHED_FIFO)
56+
return attr
57+
}()
58+
59+
init?(common com: Common) {
60+
common = com
61+
62+
pthread_create(&thread, &threadAttr, entryC, MPVHelper.bridge(obj: self))
63+
if thread == nil {
64+
common.log.sendWarning("Couldn't create pthread for high precision timer")
65+
return nil
66+
}
67+
68+
threadPort = pthread_mach_thread_np(thread)
69+
}
70+
71+
func updatePolicy(periodSeconds: Double = 1 / 60.0) {
72+
let period = periodSeconds * nanoPerSecond / machToNano
73+
var policy = thread_time_constraint_policy(
74+
period: UInt32(period),
75+
computation: UInt32(0.75 * period),
76+
constraint: UInt32(0.85 * period),
77+
preemptible: 1
78+
)
79+
80+
let success = withUnsafeMutablePointer(to: &policy) {
81+
$0.withMemoryRebound(to: integer_t.self, capacity: policyCount) {
82+
thread_policy_set(threadPort, policyFlavor, $0, typeNumber)
83+
}
84+
}
85+
86+
isHighPrecision = success == KERN_SUCCESS
87+
if !isHighPrecision {
88+
common.log.sendWarning("Couldn't create a high precision timer")
89+
}
90+
}
91+
92+
func terminate() {
93+
condition.lock()
94+
isRunning = false
95+
condition.signal()
96+
condition.unlock()
97+
pthread_kill(thread, SIGALRM)
98+
pthread_join(thread, nil)
99+
}
100+
101+
func scheduleAt(time: UInt64, closure: @escaping () -> ()) {
102+
condition.lock()
103+
let firstEventTime = events.first?.time ?? 0
104+
let lastEventTime = events.last?.time ?? 0
105+
events.append(Timing(time: time, closure: closure))
106+
107+
if lastEventTime > time {
108+
events.sort{ $0.time < $1.time }
109+
}
110+
111+
condition.signal()
112+
condition.unlock()
113+
114+
if firstEventTime > time {
115+
pthread_kill(thread, SIGALRM)
116+
}
117+
}
118+
119+
let threadSignal: @convention(c) (Int32) -> () = { (sig: Int32) in }
120+
121+
let entryC: @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? = { (ptr: UnsafeMutableRawPointer) in
122+
let ptimer: PreciseTimer = MPVHelper.bridge(ptr: ptr)
123+
ptimer.entry()
124+
return nil
125+
}
126+
127+
func entry() {
128+
signal(SIGALRM, threadSignal)
129+
130+
while isRunning {
131+
condition.lock()
132+
while events.count == 0 && isRunning {
133+
condition.wait()
134+
}
135+
136+
if !isRunning { break }
137+
138+
guard let event = events.first else {
139+
continue
140+
}
141+
condition.unlock()
142+
143+
mach_wait_until(event.time)
144+
145+
condition.lock()
146+
if events.first?.time == event.time && isRunning {
147+
event.closure()
148+
events.removeFirst()
149+
}
150+
condition.unlock()
151+
}
152+
}
153+
}

osdep/macosx_application.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ enum {
2626
FRAME_WHOLE,
2727
};
2828

29+
enum {
30+
RENDER_TIMER_CALLBACK = 0,
31+
RENDER_TIMER_PRECISE,
32+
RENDER_TIMER_SYSTEM,
33+
};
34+
2935
struct macos_opts {
3036
int macos_title_bar_style;
3137
int macos_title_bar_appearance;
@@ -35,6 +41,7 @@ struct macos_opts {
3541
bool macos_force_dedicated_gpu;
3642
int macos_app_activation_policy;
3743
int macos_geometry_calculation;
44+
int macos_render_timer;
3845
int cocoa_cb_sw_renderer;
3946
bool cocoa_cb_10bit_context;
4047
};

osdep/macosx_application.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
{"regular", 0}, {"accessory", 1}, {"prohibited", 2})},
6868
{"macos-geometry-calculation", OPT_CHOICE(macos_geometry_calculation,
6969
{"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
70+
{"macos-render-timer", OPT_CHOICE(macos_render_timer,
71+
{"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
72+
{"system", RENDER_TIMER_SYSTEM})},
7073
{"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
7174
{"auto", -1}, {"no", 0}, {"yes", 1})},
7275
{"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},

video/out/gpu/context.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ extern const struct ra_ctx_fns ra_ctx_vulkan_win;
5151
extern const struct ra_ctx_fns ra_ctx_vulkan_xlib;
5252
extern const struct ra_ctx_fns ra_ctx_vulkan_android;
5353
extern const struct ra_ctx_fns ra_ctx_vulkan_display;
54+
extern const struct ra_ctx_fns ra_ctx_vulkan_mac;
5455

5556
/* Direct3D 11 */
5657
extern const struct ra_ctx_fns ra_ctx_d3d11;
@@ -113,6 +114,9 @@ static const struct ra_ctx_fns *contexts[] = {
113114
#if HAVE_VK_KHR_DISPLAY
114115
&ra_ctx_vulkan_display,
115116
#endif
117+
#if HAVE_COCOA && HAVE_SWIFT
118+
&ra_ctx_vulkan_mac,
119+
#endif
116120
#endif
117121

118122
/* No API contexts: */

video/out/mac/metal_layer.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of mpv.
3+
*
4+
* mpv is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* mpv is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import Cocoa
19+
20+
class MetalLayer: CAMetalLayer {
21+
unowned var common: MacCommon
22+
23+
init(common com: MacCommon) {
24+
common = com
25+
super.init()
26+
27+
pixelFormat = .rgba16Float
28+
backgroundColor = NSColor.black.cgColor
29+
}
30+
31+
// necessary for when the layer containing window changes the screen
32+
override init(layer: Any) {
33+
guard let oldLayer = layer as? MetalLayer else {
34+
fatalError("init(layer: Any) passed an invalid layer")
35+
}
36+
common = oldLayer.common
37+
super.init()
38+
}
39+
40+
required init?(coder: NSCoder) {
41+
fatalError("init(coder:) has not been implemented")
42+
}
43+
}

0 commit comments

Comments
 (0)