summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulian <MyFakeAcc.4@googlemail.com>2016-09-12 02:30:09 +0200
committerwm4 <wm4@nowhere>2017-10-09 20:40:31 +0200
commit5a9ccfdcb8d130b697a4c7c3635be05f7cac9c7e (patch)
tree3bd26fbc3cc661352b083fe6c837ea5463e5f7aa
parent78cbe40fa55e325ed8f3d0541be1200b34a1e37e (diff)
downloadmpv-5a9ccfdcb8d130b697a4c7c3635be05f7cac9c7e.tar.bz2
mpv-5a9ccfdcb8d130b697a4c7c3635be05f7cac9c7e.tar.xz
stats: add vsync-ratio and vsync-jitter
Both come with graphs enabled by default. They can be disabled using the options plot_vsync_ratio and plot_vsync_jitter (see the readme regarding customization). In case both graphs are deactivated the ratio and jitter properties will be printed on the same line to save some space. One more user-visible/noticeable change: the maximum used to scale the values for each graph is no longer calculated each time the stats are shown but instead when collecting the data. This means the maximum can actually be higher than all values currently present in a graph's data buffer. This makes it easier to judge a graph's values, especially since as we have no axis labels. To prevent high values staying max forever graph data buffers are cleared when toggling (can be disabled). It was never useful to keep old data and simply append newer data (with a time skip) anyway.
-rw-r--r--player/lua/stats.lua207
1 files changed, 138 insertions, 69 deletions
diff --git a/player/lua/stats.lua b/player/lua/stats.lua
index 000708c876..1b3a61d739 100644
--- a/player/lua/stats.lua
+++ b/player/lua/stats.lua
@@ -24,9 +24,12 @@ local o = {
debug = false,
-- Graph options and style
- plot_graphs = true,
+ plot_perfdata = true,
+ plot_vsync_ratio = true,
+ plot_vsync_jitter = true,
skip_frames = 5,
global_max = true,
+ flush_graph_data = true, -- clear data buffers when toggling
plot_bg_border_color = "0000FF",
plot_bg_color = "262626",
plot_color = "FFFFFF",
@@ -65,12 +68,7 @@ local o = {
options.read_options(o)
local format = string.format
--- Buffer for the "last" value of performance data for render/present/upload
-local plast = {{0}, {0}, {0}}
--- Position in buffer
-local ppos = 1
--- Length of buffer
-local plen = 50
+local max = math.max
-- Function used to record performance data
local recorder = nil
-- Timer used for toggling
@@ -79,6 +77,18 @@ local timer = nil
local ass_start = mp.get_property_osd("osd-ass-cc/0")
local ass_stop = mp.get_property_osd("osd-ass-cc/1")
+-- Ring buffers for the values used to construct a graph.
+-- .pos denotes the current position, .len the buffer length
+-- .max is the max value in the corresponding buffer as computed in record_data().
+-- `plast_buf` is a table of buffers for the "last" value of performance data
+-- for render/present/upload.
+local plast_buf, vsratio_buf, vsjitter_buf
+local function init_buffers()
+ plast_buf = {{0, max = 0}, {0, max = 0}, {0, max = 0}, pos = 1, len = 50}
+ vsratio_buf = {0, pos = 1, len = 50, max = 0}
+ vsjitter_buf = {0, pos = 1, len = 50, max = 0}
+end
+
local function set_ASS(b)
if not o.ass_formatting then
@@ -138,6 +148,7 @@ local function has_ansi()
return true
end
+
-- Generate a graph from the given values.
-- Returns an ASS formatted vector drawing as string.
--
@@ -149,8 +160,8 @@ end
-- values to a range of 0 to `v_max`.
-- scale : A value that will be multiplied with all data values.
local function generate_graph(values, i, len, v_max, scale)
- -- check if at least one value was recorded yet (we assume lua-style 1-indexing)
- if i < 1 then
+ -- Check if at least one value exists
+ if not values[i] then
return ""
end
@@ -180,6 +191,43 @@ local function generate_graph(values, i, len, v_max, scale)
end
+-- Format and append a property.
+-- A property whose value is either `nil` or empty (hereafter called "invalid")
+-- is skipped and not appended.
+-- Returns `false` in case nothing was appended, otherwise `true`.
+--
+-- s : Table containing strings.
+-- prop : The property to query and format (based on its OSD representation).
+-- attr : Optional table to overwrite certain (formatting) attributes for
+-- this property.
+-- exclude: Optional table containing keys which are considered invalid values
+-- for this property. Specifying this will replace empty string as
+-- default invalid value (nil is always invalid).
+local function append_property(s, prop, attr, excluded)
+ excluded = excluded or {[""] = true}
+ local ret = mp.get_property_osd(prop)
+ if not ret or excluded[ret] then
+ if o.debug then
+ print("No value for property: " .. prop)
+ end
+ return false
+ end
+
+ attr.prefix_sep = attr.prefix_sep or o.prefix_sep
+ attr.indent = attr.indent or o.indent
+ attr.nl = attr.nl or o.nl
+ attr.suffix = attr.suffix or ""
+ attr.prefix = attr.prefix or ""
+ attr.no_prefix_markup = attr.no_prefix_markup or false
+ attr.prefix = attr.no_prefix_markup and attr.prefix or b(attr.prefix)
+ ret = attr.no_value and "" or ret
+
+ s[#s+1] = format("%s%s%s%s%s%s", attr.nl, attr.indent,
+ attr.prefix, attr.prefix_sep, no_ASS(ret), attr.suffix)
+ return true
+end
+
+
local function append_perfdata(s)
local vo_p = mp.get_property_native("vo-performance")
if not vo_p then
@@ -194,7 +242,7 @@ local function append_perfdata(s)
local avg_s = vo_p["render-avg"] + vo_p["present-avg"] + vo_p["upload-avg"]
local peak_s = vo_p["render-peak"] + vo_p["present-peak"] + vo_p["upload-peak"]
- -- highlight i with a red border when t exceeds the time for one frame
+ -- Highlight i with a red border when t exceeds the time for one frame
-- or yellow when it exceeds a given threshold
local function hl(i, t)
if o.timing_warning and target_fps > 0 then
@@ -212,21 +260,17 @@ local function append_perfdata(s)
local rsuffix, psuffix, usuffix
- if o.plot_graphs and o.ass_formatting and timer:is_enabled() then
- local max = {1, 1, 1}
- for e = 1, plen do
- if plast[1][e] and plast[1][e] > max[1] then max[1] = plast[1][e] end
- if plast[2][e] and plast[2][e] > max[2] then max[2] = plast[2][e] end
- if plast[3][e] and plast[3][e] > max[3] then max[3] = plast[3][e] end
- end
+ -- Plot graphs when configured and we are toggled
+ if o.plot_perfdata and o.ass_formatting and timer:is_enabled() then
+ local pmax = {plast_buf[1].max, plast_buf[2].max, plast_buf[3].max}
if o.global_max then
- max[1] = math.max(max[1], max[2], max[3])
- max[2], max[3] = max[1], max[1]
+ pmax[1] = max(pmax[1], pmax[2], pmax[3])
+ pmax[2], pmax[3] = pmax[1], pmax[1]
end
- rsuffix = generate_graph(plast[1], ppos, plen, max[1], 0.8)
- psuffix = generate_graph(plast[2], ppos, plen, max[2], 0.8)
- usuffix = generate_graph(plast[3], ppos, plen, max[3], 0.8)
+ rsuffix = generate_graph(plast_buf[1], plast_buf.pos, plast_buf.len, pmax[1], 0.8)
+ psuffix = generate_graph(plast_buf[2], plast_buf.pos, plast_buf.len, pmax[2], 0.8)
+ usuffix = generate_graph(plast_buf[3], plast_buf.pos, plast_buf.len, pmax[3], 0.8)
s[#s+1] = format("%s%s%s%s{\\fs%s}%s%s%s{\\fs%s}", o.nl, o.indent,
b("Frame Timings:"), o.prefix_sep, o.font_size * 0.66,
@@ -260,40 +304,42 @@ local function append_perfdata(s)
end
--- Format and append a property.
--- A property whose value is either `nil` or empty (hereafter called "invalid")
--- is skipped and not appended.
--- Returns `false` in case nothing was appended, otherwise `true`.
---
--- s : Table containing strings.
--- prop : The property to query and format (based on its OSD representation).
--- attr : Optional table to overwrite certain (formatting) attributes for
--- this property.
--- exclude: Optional table containing keys which are considered invalid values
--- for this property. Specifying this will replace empty string as
--- default invalid value (nil is always invalid).
-local function append_property(s, prop, attr, excluded)
- excluded = excluded or {[""] = true}
- local ret = mp.get_property_osd(prop)
- if not ret or excluded[ret] then
- if o.debug then
- print("No value for property: " .. prop)
- end
- return false
+function append_display_sync(s)
+ if not mp.get_property_bool("display-sync-active", false) then
+ return
end
- attr.prefix_sep = attr.prefix_sep or o.prefix_sep
- attr.indent = attr.indent or o.indent
- attr.nl = attr.nl or o.nl
- attr.suffix = attr.suffix or ""
- attr.prefix = attr.prefix or ""
- attr.no_prefix_markup = attr.no_prefix_markup or false
- attr.prefix = attr.no_prefix_markup and attr.prefix or b(attr.prefix)
- ret = attr.no_value and "" or ret
+ local vspeed = append_property(s, "video-speed-correction", {prefix="DS:"})
+ if vspeed then
+ append_property(s, "audio-speed-correction",
+ {prefix="/", nl="", indent=" ", prefix_sep=" ", no_prefix_markup=true})
+ else
+ append_property(s, "audio-speed-correction",
+ {prefix="DS:" .. o.prefix_sep .. " - / ", prefix_sep=""})
+ end
- s[#s+1] = format("%s%s%s%s%s%s", attr.nl, attr.indent,
- attr.prefix, attr.prefix_sep, no_ASS(ret), attr.suffix)
- return true
+ -- Since no graph is needed we can print ratio/jitter on the same line and save some space
+ if not (o.plot_vsync_ratio or o.plot_vsync_jitter) then
+ local vratio = append_property(s, "vsync-ratio", {prefix="VSync Ratio:"})
+ append_property(s, "vsync-jitter", {prefix="VSync Jitter:", nl=vratio and "" or o.nl})
+ return
+ end
+
+ -- As we potentially need to plot some graphs we print jitter and ratio on
+ -- their own lines so we have the same layout when toggled (= drawing graphs)
+ local ratio_graph = ""
+ local jitter_graph = ""
+ if o.ass_formatting and timer:is_enabled() then
+ if o.plot_vsync_ratio then
+ ratio_graph = generate_graph(vsratio_buf, vsratio_buf.pos, vsratio_buf.len, vsratio_buf.max, 0.8)
+ end
+ if o.plot_vsync_jitter then
+ jitter_graph = generate_graph(vsjitter_buf, vsjitter_buf.pos, vsjitter_buf.len, vsjitter_buf.max, 0.8)
+ end
+ end
+
+ append_property(s, "vsync-ratio", {prefix="VSync Ratio:", suffix=o.prefix_sep .. ratio_graph})
+ append_property(s, "vsync-jitter", {prefix="VSync Jitter:", suffix=o.prefix_sep .. jitter_graph})
end
@@ -351,11 +397,8 @@ local function add_video(s)
append_property(s, "estimated-vf-fps",
{prefix="FPS:", suffix=" (estimated)"})
end
- if append_property(s, "video-speed-correction", {prefix="DS:"}, {["+0.00000%"]=true}) then
- append_property(s, "audio-speed-correction",
- {prefix="/", nl="", indent=" ", prefix_sep=" ", no_prefix_markup=true})
- end
+ append_display_sync(s)
append_perfdata(s)
if append_property(s, "video-params/w", {prefix="Native Resolution:"}) then
@@ -429,8 +472,9 @@ local function print_stats(duration)
end
-local function record_perfdata(skip)
- skip = math.max(skip, 0)
+local function record_data(skip)
+ init_buffers()
+ skip = max(skip, 0)
local i = skip
return function()
if i < skip then
@@ -440,28 +484,53 @@ local function record_perfdata(skip)
i = 0
end
- local vo_p = mp.get_property_native("vo-performance")
- if not vo_p then
- return
+ if o.plot_perfdata then
+ local vo_p = mp.get_property_native("vo-performance")
+ if vo_p then
+ plast_buf.pos = (plast_buf.pos % plast_buf.len) + 1
+ plast_buf[1][plast_buf.pos] = vo_p["render-last"]
+ plast_buf[1].max = max(plast_buf[1].max, plast_buf[1][plast_buf.pos])
+ plast_buf[2][plast_buf.pos] = vo_p["present-last"]
+ plast_buf[2].max = max(plast_buf[2].max, plast_buf[2][plast_buf.pos])
+ plast_buf[3][plast_buf.pos] = vo_p["upload-last"]
+ plast_buf[3].max = max(plast_buf[3].max, plast_buf[3][plast_buf.pos])
+ end
+ end
+
+ if o.plot_vsync_jitter then
+ local r = mp.get_property_number("vsync-jitter", nil)
+ if r then
+ vsjitter_buf.pos = (vsjitter_buf.pos % vsjitter_buf.len) + 1
+ vsjitter_buf[vsjitter_buf.pos] = r
+ vsjitter_buf.max = max(vsjitter_buf.max, r)
+ end
+ end
+
+ if o.plot_vsync_ratio then
+ local r = mp.get_property_number("vsync-ratio", nil)
+ if r then
+ vsratio_buf.pos = (vsratio_buf.pos % vsratio_buf.len) + 1
+ vsratio_buf[vsratio_buf.pos] = r
+ vsratio_buf.max = max(vsratio_buf.max, r)
+ end
end
- ppos = (ppos % plen) + 1
- plast[1][ppos] = vo_p["render-last"]
- plast[2][ppos] = vo_p["present-last"]
- plast[3][ppos] = vo_p["upload-last"]
end
end
local function toggle_stats()
+ -- Disable
if timer:is_enabled() then
- if o.plot_graphs then
+ if recorder then
mp.unregister_event(recorder)
+ recorder = nil
end
timer:kill()
mp.osd_message("", 0)
+ -- Enable
else
- if o.plot_graphs then
- recorder = record_perfdata(o.skip_frames)
+ if o.plot_perfdata or o.plot_vsync_jitter or o.plot_vsync_ratio then
+ recorder = record_data(o.skip_frames)
mp.register_event("tick", recorder)
end
timer:resume()