mirror of
https://github.com/ARM-software/devlib.git
synced 2025-01-31 02:00:45 +00:00
derived: add DerivedSurfaceFlingerStats
Add DerivedSurfaceFlingerStats that parse output from SurfaceFlingerFramesInstrument to produce FPS data and rendering statistics.
This commit is contained in:
parent
f692315d9c
commit
9f666320f3
@ -13,7 +13,7 @@ from devlib.instrument import Instrument, InstrumentChannel, Measurement, Measur
|
|||||||
from devlib.instrument import MEASUREMENT_TYPES, INSTANTANEOUS, CONTINUOUS
|
from devlib.instrument import MEASUREMENT_TYPES, INSTANTANEOUS, CONTINUOUS
|
||||||
from devlib.instrument.daq import DaqInstrument
|
from devlib.instrument.daq import DaqInstrument
|
||||||
from devlib.instrument.energy_probe import EnergyProbeInstrument
|
from devlib.instrument.energy_probe import EnergyProbeInstrument
|
||||||
from devlib.instrument.frames import GfxInfoFramesInstrument
|
from devlib.instrument.frames import GfxInfoFramesInstrument, SurfaceFlingerFramesInstrument
|
||||||
from devlib.instrument.hwmon import HwmonInstrument
|
from devlib.instrument.hwmon import HwmonInstrument
|
||||||
from devlib.instrument.monsoon import MonsoonInstrument
|
from devlib.instrument.monsoon import MonsoonInstrument
|
||||||
from devlib.instrument.netstats import NetstatsInstrument
|
from devlib.instrument.netstats import NetstatsInstrument
|
||||||
@ -21,7 +21,7 @@ from devlib.instrument.gem5power import Gem5PowerInstrument
|
|||||||
|
|
||||||
from devlib.derived import DerivedMeasurements, DerivedMetric
|
from devlib.derived import DerivedMeasurements, DerivedMetric
|
||||||
from devlib.derived.energy import DerivedEnergyMeasurements
|
from devlib.derived.energy import DerivedEnergyMeasurements
|
||||||
from devlib.derived.fps import DerivedGfxInfoStats
|
from devlib.derived.fps import DerivedGfxInfoStats, DerivedSurfaceFlingerStats
|
||||||
|
|
||||||
from devlib.trace.ftrace import FtraceCollector
|
from devlib.trace.ftrace import FtraceCollector
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ except ImportError:
|
|||||||
pd = None
|
pd = None
|
||||||
|
|
||||||
from devlib import DerivedMeasurements, DerivedMetric, MeasurementsCsv, InstrumentChannel
|
from devlib import DerivedMeasurements, DerivedMetric, MeasurementsCsv, InstrumentChannel
|
||||||
|
from devlib.exception import HostError
|
||||||
from devlib.utils.rendering import gfxinfo_get_last_dump, VSYNC_INTERVAL
|
from devlib.utils.rendering import gfxinfo_get_last_dump, VSYNC_INTERVAL
|
||||||
from devlib.utils.types import numeric
|
from devlib.utils.types import numeric
|
||||||
|
|
||||||
@ -135,3 +136,79 @@ class DerivedGfxInfoStats(DerivedFpsStats):
|
|||||||
DerivedMetric('total_frames', frame_count, 'frames'),
|
DerivedMetric('total_frames', frame_count, 'frames'),
|
||||||
MeasurementsCsv(csv_file)]
|
MeasurementsCsv(csv_file)]
|
||||||
|
|
||||||
|
|
||||||
|
class DerivedSurfaceFlingerStats(DerivedFpsStats):
|
||||||
|
|
||||||
|
def _process_with_pandas(self, measurements_csv):
|
||||||
|
data = pd.read_csv(measurements_csv.path)
|
||||||
|
|
||||||
|
# fiter out bogus frames.
|
||||||
|
bogus_frames_filter = data.actual_present_time_us != 0x7fffffffffffffff
|
||||||
|
actual_present_times = data.actual_present_time_us[bogus_frames_filter]
|
||||||
|
actual_present_time_deltas = actual_present_times.diff().dropna()
|
||||||
|
|
||||||
|
vsyncs_to_compose = actual_present_time_deltas.div(VSYNC_INTERVAL)
|
||||||
|
vsyncs_to_compose.apply(lambda x: int(round(x, 0)))
|
||||||
|
|
||||||
|
# drop values lower than drop_threshold FPS as real in-game frame
|
||||||
|
# rate is unlikely to drop below that (except on loading screens
|
||||||
|
# etc, which should not be factored in frame rate calculation).
|
||||||
|
per_frame_fps = (1.0 / (vsyncs_to_compose.multiply(VSYNC_INTERVAL / 1e9)))
|
||||||
|
keep_filter = per_frame_fps > self.drop_threshold
|
||||||
|
filtered_vsyncs_to_compose = vsyncs_to_compose[keep_filter]
|
||||||
|
per_frame_fps.name = 'fps'
|
||||||
|
|
||||||
|
csv_file = self._get_csv_file_name(measurements_csv.path)
|
||||||
|
per_frame_fps.to_csv(csv_file, index=False, header=True)
|
||||||
|
|
||||||
|
if not filtered_vsyncs_to_compose.empty:
|
||||||
|
fps = 0
|
||||||
|
total_vsyncs = filtered_vsyncs_to_compose.sum()
|
||||||
|
frame_count = filtered_vsyncs_to_compose.size
|
||||||
|
|
||||||
|
if total_vsyncs:
|
||||||
|
fps = 1e9 * frame_count / (VSYNC_INTERVAL * total_vsyncs)
|
||||||
|
|
||||||
|
janks = self._calc_janks(filtered_vsyncs_to_compose)
|
||||||
|
not_at_vsync = self._calc_not_at_vsync(vsyncs_to_compose)
|
||||||
|
else:
|
||||||
|
fps = 0
|
||||||
|
frame_count = 0
|
||||||
|
janks = 0
|
||||||
|
not_at_vsync = 0
|
||||||
|
|
||||||
|
return [DerivedMetric('fps', fps, 'fps'),
|
||||||
|
DerivedMetric('total_frames', frame_count, 'frames'),
|
||||||
|
MeasurementsCsv(csv_file),
|
||||||
|
DerivedMetric('janks', janks, 'count'),
|
||||||
|
DerivedMetric('janks_pc', janks * 100 / frame_count, 'percent'),
|
||||||
|
DerivedMetric('missed_vsync', not_at_vsync, 'count')]
|
||||||
|
|
||||||
|
def _process_without_pandas(self, measurements_csv):
|
||||||
|
# Given that SurfaceFlinger has been deprecated in favor of GfxInfo,
|
||||||
|
# it does not seem worth it implementing this.
|
||||||
|
raise HostError('Please install "pandas" Python package to process SurfaceFlinger frames')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calc_janks(filtered_vsyncs_to_compose):
|
||||||
|
"""
|
||||||
|
Internal method for calculating jank frames.
|
||||||
|
"""
|
||||||
|
pause_latency = 20
|
||||||
|
vtc_deltas = filtered_vsyncs_to_compose.diff().dropna()
|
||||||
|
vtc_deltas = vtc_deltas.abs()
|
||||||
|
janks = vtc_deltas.apply(lambda x: (pause_latency > x > 1.5) and 1 or 0).sum()
|
||||||
|
|
||||||
|
return janks
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calc_not_at_vsync(vsyncs_to_compose):
|
||||||
|
"""
|
||||||
|
Internal method for calculating the number of frames that did not
|
||||||
|
render in a single vsync cycle.
|
||||||
|
"""
|
||||||
|
epsilon = 0.0001
|
||||||
|
func = lambda x: (abs(x - 1.0) > epsilon) and 1 or 0
|
||||||
|
not_at_vsync = vsyncs_to_compose.apply(func).sum()
|
||||||
|
|
||||||
|
return not_at_vsync
|
||||||
|
@ -179,3 +179,43 @@ FPS / Rendering
|
|||||||
Please see the `gfxinfo documentation`_ for details.
|
Please see the `gfxinfo documentation`_ for details.
|
||||||
|
|
||||||
.. _gfxinfo documentation: https://developer.android.com/training/testing/performance.html
|
.. _gfxinfo documentation: https://developer.android.com/training/testing/performance.html
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: DerivedSurfaceFlingerStats(drop_threshold=5, suffix='-fps', filename=None, outdir=None)
|
||||||
|
|
||||||
|
Produces FPS (frames-per-second) and other dervied statistics from
|
||||||
|
:class:`SurfaceFlingerFramesInstrument` output. This takes several optional
|
||||||
|
parameters in creation:
|
||||||
|
|
||||||
|
:param drop_threshold: FPS in an application, such as a game, which this
|
||||||
|
processor is primarily targeted at, cannot reasonably
|
||||||
|
drop to a very low value. This is specified to this
|
||||||
|
threhold. If an FPS for a frame is computed to be
|
||||||
|
lower than this treshold, it will be dropped on the
|
||||||
|
assumption that frame rednering was suspended by the
|
||||||
|
system (e.g. when idling), or there was some sort of
|
||||||
|
error, and therefore this should be used in
|
||||||
|
performance calculations. defaults to ``5``.
|
||||||
|
:param suffix: The name of the gerated per-frame FPS csv file will be
|
||||||
|
derived from the input frames csv file by appending this
|
||||||
|
suffix. This cannot be specified at the same time as
|
||||||
|
a ``filename``.
|
||||||
|
:param filename: As an alternative to the suffix, a complete file name for
|
||||||
|
FPS csv can be specified. This cannot be used at the same
|
||||||
|
time as the ``suffix``.
|
||||||
|
:param outdir: By default, the FPS csv file will be placed in the same
|
||||||
|
directory as the input frames csv file. This can be changed
|
||||||
|
by specifying an alternate directory here
|
||||||
|
|
||||||
|
.. warning:: Specifying both ``filename`` and ``oudir`` will mean that exactly
|
||||||
|
the same file will be used for FPS output on each invocation of
|
||||||
|
``process()`` (even for different inputs) resulting in previous
|
||||||
|
results being overwritten.
|
||||||
|
|
||||||
|
.. method:: DerivedSurfaceFlingerStats.process(measurement_csv)
|
||||||
|
|
||||||
|
Process the fames csv generated by :class:`SurfaceFlingerFramesInstrument` and
|
||||||
|
returns a list containing exactly three entries: :class:`DerivedMetric`\ s
|
||||||
|
``fps`` and ``total_frames``, followed by a :class:`MeasurentCsv` containing
|
||||||
|
per-frame FPSs values, followed by ``janks`` ``janks_pc``, and
|
||||||
|
``missed_vsync`` metrics.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user