mirror of https://github.com/mpv-player/mpv
mac/vulkan: add support for frame timing via presentation feedback
This commit is contained in:
parent
6df07ce90c
commit
ee6794225d
|
@ -6358,6 +6358,7 @@ them.
|
||||||
:precise: Syncs to the time of the next vertical display refresh reported by the
|
:precise: Syncs to the time of the next vertical display refresh reported by the
|
||||||
CVDisplayLink callback provided information
|
CVDisplayLink callback provided information
|
||||||
:system: No manual syncing, depend on the layer mechanic and the next drawable
|
:system: No manual syncing, depend on the layer mechanic and the next drawable
|
||||||
|
:feedback: Same as precise but uses the presentation feedback core mechanism
|
||||||
|
|
||||||
``--android-surface-size=<WxH>``
|
``--android-surface-size=<WxH>``
|
||||||
Set dimensions of the rendering surface used by the Android gpu context.
|
Set dimensions of the rendering surface used by the Android gpu context.
|
||||||
|
|
|
@ -1541,6 +1541,7 @@ if features['cocoa'] and features['swift']
|
||||||
'osdep/mac/menu_bar.swift',
|
'osdep/mac/menu_bar.swift',
|
||||||
'osdep/mac/option_helper.swift',
|
'osdep/mac/option_helper.swift',
|
||||||
'osdep/mac/precise_timer.swift',
|
'osdep/mac/precise_timer.swift',
|
||||||
|
'osdep/mac/presentation.swift',
|
||||||
'osdep/mac/swift_compat.swift',
|
'osdep/mac/swift_compat.swift',
|
||||||
'osdep/mac/swift_extensions.swift',
|
'osdep/mac/swift_extensions.swift',
|
||||||
'osdep/mac/type_helper.swift',
|
'osdep/mac/type_helper.swift',
|
||||||
|
|
|
@ -29,9 +29,10 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
RENDER_TIMER_CALLBACK = 0,
|
RENDER_TIMER_PRESENTATION_FEEDBACK = -1,
|
||||||
RENDER_TIMER_PRECISE,
|
|
||||||
RENDER_TIMER_SYSTEM,
|
RENDER_TIMER_SYSTEM,
|
||||||
|
RENDER_TIMER_CALLBACK,
|
||||||
|
RENDER_TIMER_PRECISE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct macos_opts {
|
struct macos_opts {
|
||||||
|
|
|
@ -51,7 +51,7 @@ const struct m_sub_options macos_conf = {
|
||||||
{"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
|
{"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
|
||||||
{"macos-render-timer", OPT_CHOICE(macos_render_timer,
|
{"macos-render-timer", OPT_CHOICE(macos_render_timer,
|
||||||
{"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
|
{"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
|
||||||
{"system", RENDER_TIMER_SYSTEM})},
|
{"system", RENDER_TIMER_SYSTEM}, {"feedback", RENDER_TIMER_PRESENTATION_FEEDBACK})},
|
||||||
{"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
|
{"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
|
||||||
{"auto", -1}, {"no", 0}, {"yes", 1})},
|
{"auto", -1}, {"no", 0}, {"yes", 1})},
|
||||||
{"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},
|
{"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},
|
||||||
|
@ -61,6 +61,7 @@ const struct m_sub_options macos_conf = {
|
||||||
.defaults = &(const struct macos_opts){
|
.defaults = &(const struct macos_opts){
|
||||||
.macos_title_bar_color = {0, 0, 0, 0},
|
.macos_title_bar_color = {0, 0, 0, 0},
|
||||||
.macos_fs_animation_duration = -1,
|
.macos_fs_animation_duration = -1,
|
||||||
|
.macos_render_timer = RENDER_TIMER_CALLBACK,
|
||||||
.cocoa_cb_sw_renderer = -1,
|
.cocoa_cb_sw_renderer = -1,
|
||||||
.cocoa_cb_10bit_context = true
|
.cocoa_cb_10bit_context = true
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extension Presentation {
|
||||||
|
struct Time {
|
||||||
|
let cvTime: CVTimeStamp
|
||||||
|
var skipped: Int64 = 0
|
||||||
|
var time: Int64 { return mp_time_ns_from_raw_time(mp_raw_time_ns_from_mach(cvTime.hostTime)) }
|
||||||
|
var duration: Int64 {
|
||||||
|
let durationSeconds = Double(cvTime.videoRefreshPeriod) / Double(cvTime.videoTimeScale)
|
||||||
|
return Int64(durationSeconds * Presentation.nanoPerSecond * cvTime.rateScalar)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ time: CVTimeStamp) {
|
||||||
|
cvTime = time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Presentation {
|
||||||
|
unowned var common: Common
|
||||||
|
var times: [Time] = []
|
||||||
|
static let nanoPerSecond: Double = 1e+9
|
||||||
|
|
||||||
|
init(common com: Common) {
|
||||||
|
common = com
|
||||||
|
}
|
||||||
|
|
||||||
|
func add(time: CVTimeStamp) {
|
||||||
|
times.append(Time(time))
|
||||||
|
}
|
||||||
|
|
||||||
|
func next() -> Time? {
|
||||||
|
let now = mp_time_ns()
|
||||||
|
let count = times.count
|
||||||
|
times.removeAll(where: { $0.time <= now })
|
||||||
|
var time = times.first
|
||||||
|
time?.skipped = Int64(max(count - times.count - 1, 0))
|
||||||
|
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,12 @@ void mp_sleep_ns(int64_t ns)
|
||||||
|
|
||||||
uint64_t mp_raw_time_ns(void)
|
uint64_t mp_raw_time_ns(void)
|
||||||
{
|
{
|
||||||
return mach_absolute_time() * timebase_ratio_ns;
|
return mp_raw_time_ns_from_mach(mach_absolute_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t mp_raw_time_ns_from_mach(uint64_t mach_time)
|
||||||
|
{
|
||||||
|
return mach_time * timebase_ratio_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_raw_time_init(void)
|
void mp_raw_time_init(void)
|
||||||
|
|
|
@ -46,7 +46,12 @@ void mp_time_init(void)
|
||||||
|
|
||||||
int64_t mp_time_ns(void)
|
int64_t mp_time_ns(void)
|
||||||
{
|
{
|
||||||
return mp_raw_time_ns() - raw_time_offset;
|
return mp_time_ns_from_raw_time(mp_raw_time_ns());
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t mp_time_ns_from_raw_time(uint64_t raw_time)
|
||||||
|
{
|
||||||
|
return raw_time - raw_time_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
double mp_time_sec(void)
|
double mp_time_sec(void)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define MPLAYER_TIMER_H
|
#define MPLAYER_TIMER_H
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
// Initialize timer, must be called at least once at start.
|
// Initialize timer, must be called at least once at start.
|
||||||
void mp_time_init(void);
|
void mp_time_init(void);
|
||||||
|
@ -26,6 +27,9 @@ void mp_time_init(void);
|
||||||
// Return time in nanoseconds. Never wraps. Never returns negative values.
|
// Return time in nanoseconds. Never wraps. Never returns negative values.
|
||||||
int64_t mp_time_ns(void);
|
int64_t mp_time_ns(void);
|
||||||
|
|
||||||
|
// Return time in nanoseconds. Coverts raw time in nanoseconds to mp time, subtracts init offset.
|
||||||
|
int64_t mp_time_ns_from_raw_time(uint64_t raw_time);
|
||||||
|
|
||||||
// Return time in seconds. Can have down to 1 nanosecond resolution, but will
|
// Return time in seconds. Can have down to 1 nanosecond resolution, but will
|
||||||
// be much worse when casted to float.
|
// be much worse when casted to float.
|
||||||
double mp_time_sec(void);
|
double mp_time_sec(void);
|
||||||
|
@ -38,6 +42,11 @@ uint64_t mp_raw_time_ns(void);
|
||||||
// Sleep in nanoseconds.
|
// Sleep in nanoseconds.
|
||||||
void mp_sleep_ns(int64_t ns);
|
void mp_sleep_ns(int64_t ns);
|
||||||
|
|
||||||
|
#if HAVE_DARWIN
|
||||||
|
// Coverts mach time to raw time in nanoseconds and returns it.
|
||||||
|
uint64_t mp_raw_time_ns_from_mach(uint64_t mach_time);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// returns: timer resolution in ns if needed and started successfully, else 0
|
// returns: timer resolution in ns if needed and started successfully, else 0
|
||||||
int64_t mp_start_hires_timers(int64_t wait_ns);
|
int64_t mp_start_hires_timers(int64_t wait_ns);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import Cocoa
|
||||||
class MacCommon: Common {
|
class MacCommon: Common {
|
||||||
@objc var layer: MetalLayer?
|
@objc var layer: MetalLayer?
|
||||||
|
|
||||||
|
var presentation: Presentation?
|
||||||
var timer: PreciseTimer?
|
var timer: PreciseTimer?
|
||||||
var swapTime: UInt64 = 0
|
var swapTime: UInt64 = 0
|
||||||
let swapLock: NSCondition = NSCondition()
|
let swapLock: NSCondition = NSCondition()
|
||||||
|
@ -30,6 +31,7 @@ class MacCommon: Common {
|
||||||
super.init(option, log)
|
super.init(option, log)
|
||||||
self.vo = vo
|
self.vo = vo
|
||||||
input = InputHelper(vo.pointee.input_ctx, option)
|
input = InputHelper(vo.pointee.input_ctx, option)
|
||||||
|
presentation = Presentation(common: self)
|
||||||
timer = PreciseTimer(common: self)
|
timer = PreciseTimer(common: self)
|
||||||
|
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
|
@ -89,7 +91,7 @@ class MacCommon: Common {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func swapBuffer() {
|
@objc func swapBuffer() {
|
||||||
if option.mac.macos_render_timer != RENDER_TIMER_SYSTEM {
|
if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
|
||||||
swapLock.lock()
|
swapLock.lock()
|
||||||
while(swapTime < 1) {
|
while(swapTime < 1) {
|
||||||
swapLock.wait()
|
swapLock.wait()
|
||||||
|
@ -99,6 +101,15 @@ class MacCommon: Common {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func fillVsync(info: UnsafeMutablePointer<vo_vsync_info>) {
|
||||||
|
if option.mac.macos_render_timer != RENDER_TIMER_PRESENTATION_FEEDBACK { return }
|
||||||
|
|
||||||
|
let next = presentation?.next()
|
||||||
|
info.pointee.vsync_duration = next?.duration ?? -1
|
||||||
|
info.pointee.skipped_vsyncs = next?.skipped ?? -1
|
||||||
|
info.pointee.last_queue_display_time = next?.time ?? -1
|
||||||
|
}
|
||||||
|
|
||||||
override func displayLinkCallback(_ displayLink: CVDisplayLink,
|
override func displayLinkCallback(_ displayLink: CVDisplayLink,
|
||||||
_ inNow: UnsafePointer<CVTimeStamp>,
|
_ inNow: UnsafePointer<CVTimeStamp>,
|
||||||
_ inOutputTime: UnsafePointer<CVTimeStamp>,
|
_ inOutputTime: UnsafePointer<CVTimeStamp>,
|
||||||
|
@ -112,13 +123,18 @@ class MacCommon: Common {
|
||||||
self.swapLock.unlock()
|
self.swapLock.unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if option.mac.macos_render_timer != RENDER_TIMER_SYSTEM {
|
if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
|
||||||
if let timer = self.timer, option.mac.macos_render_timer == RENDER_TIMER_PRECISE {
|
if let timer = self.timer, option.mac.macos_render_timer == RENDER_TIMER_PRECISE {
|
||||||
timer.scheduleAt(time: inOutputTime.pointee.hostTime, closure: signalSwap)
|
timer.scheduleAt(time: inOutputTime.pointee.hostTime, closure: signalSwap)
|
||||||
return kCVReturnSuccess
|
return kCVReturnSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
signalSwap()
|
signalSwap()
|
||||||
|
return kCVReturnSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
if option.mac.macos_render_timer == RENDER_TIMER_PRESENTATION_FEEDBACK {
|
||||||
|
presentation?.add(time: inOutputTime.pointee)
|
||||||
}
|
}
|
||||||
|
|
||||||
return kCVReturnSuccess
|
return kCVReturnSuccess
|
||||||
|
|
|
@ -44,6 +44,12 @@ static void mac_vk_swap_buffers(struct ra_ctx *ctx)
|
||||||
[p->vo_mac swapBuffer];
|
[p->vo_mac swapBuffer];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mac_vk_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
|
||||||
|
{
|
||||||
|
struct priv *p = ctx->priv;
|
||||||
|
[p->vo_mac fillVsyncWithInfo:info];
|
||||||
|
}
|
||||||
|
|
||||||
static bool mac_vk_init(struct ra_ctx *ctx)
|
static bool mac_vk_init(struct ra_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
struct priv *p = ctx->priv = talloc_zero(ctx, struct priv);
|
||||||
|
@ -66,6 +72,7 @@ static bool mac_vk_init(struct ra_ctx *ctx)
|
||||||
|
|
||||||
struct ra_vk_ctx_params params = {
|
struct ra_vk_ctx_params params = {
|
||||||
.swap_buffers = mac_vk_swap_buffers,
|
.swap_buffers = mac_vk_swap_buffers,
|
||||||
|
.get_vsync = mac_vk_get_vsync,
|
||||||
};
|
};
|
||||||
|
|
||||||
VkInstance inst = vk->vkinst->instance;
|
VkInstance inst = vk->vkinst->instance;
|
||||||
|
|
Loading…
Reference in New Issue