diff options
| author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2017-02-12 02:54:58 +0200 | 
|---|---|---|
| committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2017-04-15 23:46:30 +0300 | 
| commit | 94a58cd836950743bb1a16c7f48852d0cca57038 (patch) | |
| tree | 34d9e1e3e82e12bcc2bfb4713567aa74b23c323d /py/tests | |
| parent | 4eb65d9c999670bdff819340050568dd5327e49a (diff) | |
py: Add in fence test using swsync
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'py/tests')
| -rwxr-xr-x | py/tests/sync.py | 228 | 
1 files changed, 228 insertions, 0 deletions
| diff --git a/py/tests/sync.py b/py/tests/sync.py new file mode 100755 index 0000000..366996e --- /dev/null +++ b/py/tests/sync.py @@ -0,0 +1,228 @@ +#!/usr/bin/python3 + +import ctypes +import fcntl +import os +import pykms +import selectors +import sys +import time + +bar_width = 20 +bar_speed = 8 + +class Timer(object): +    timers = [] + +    def __init__(self, timeout, callback, data): +        self.timeout = time.clock_gettime(time.CLOCK_MONOTONIC) + timeout +        self.callback = callback +        self.data = data + +        print("adding timer %f" % self.timeout) +        self.timers.append(self) +        self.timers.sort(key=lambda timer: timer.timeout) + +    @classmethod +    def fire(_class): +        clk = time.clock_gettime(time.CLOCK_MONOTONIC) +        while len(_class.timers) > 0: +            timer = _class.timers[0] +            if timer.timeout > clk: +                break + +            del _class.timers[0] +            print("fireing timer %f" % timer.timeout) +            timer.callback(timer.data) + +    @classmethod +    def next_timeout(_class): +        clk = time.clock_gettime(time.CLOCK_MONOTONIC) +        if len(_class.timers) == 0 or _class.timers[0].timeout < clk: +            return None + +        return _class.timers[0].timeout - clk + + +class Timeline(object): + +    class sw_sync_create_fence_data(ctypes.Structure): +        _fields_ = [ +            ('value', ctypes.c_uint32), +            ('name', ctypes.c_char * 32), +            ('fence', ctypes.c_int32), +        ] + +    SW_SYNC_IOC_CREATE_FENCE = (3 << 30) | (ctypes.sizeof(sw_sync_create_fence_data) << 16) | (ord('W') << 8) | (0 << 0) +    SW_SYNC_IOC_INC = (1 << 30) | (ctypes.sizeof(ctypes.c_uint32) << 16) | (ord('W') << 8) | (1 << 0) + +    class SWSync(object): +        def __init__(self, fd): +            self.fd = fd +        def __del__(self): +            os.close(self.fd) + +    def __init__(self): +        self.value = 0 +        try: +            self.fd = os.open('/sys/kernel/debug/sync/sw_sync', 0); +        except: +            raise RuntimeError('Failed to open sw_sync file') + +    def close(self): +        os.close(self.fd) + +    def create_fence(self, value): +        data = self.sw_sync_create_fence_data(value = value); +        print("ioctl number %u" % self.SW_SYNC_IOC_CREATE_FENCE) +        ret = fcntl.ioctl(self.fd, self.SW_SYNC_IOC_CREATE_FENCE, data); +        if ret < 0: +            raise RuntimeError('Failed to create fence') + +        return self.SWSync(data.fence) + +    def signal(self, value): +        fcntl.ioctl(self.fd, self.SW_SYNC_IOC_INC, ctypes.c_uint32(value)) +        self.value += value + + +class FlipHandler(): +    def __init__(self, crtc, width, height): +        super().__init__() +        self.crtc = crtc +        self.timeline = Timeline() +        self.bar_xpos = 0 +        self.front_buf = 0 +        self.fb1 = pykms.DumbFramebuffer(crtc.card, width, height, "XR24"); +        self.fb2 = pykms.DumbFramebuffer(crtc.card, width, height, "XR24"); +        self.flips = 0 +        self.flips_last = 0 +        self.frame_last = 0 +        self.time_last = 0 + +    def handle_page_flip(self, frame, time): +        if self.time_last == 0: +            self.frame_last = frame +            self.time_last = time + +        # Verify that the page flip hasn't completed before the timeline got +        # signaled. +        if self.timeline.value < 2 * self.flips - 1: +            raise RuntimeError('Page flip %u for fence %u complete before timeline (%u)!' % +                               (self.flips, 2 * self.flips - 1, self.timeline.value)) + +        self.flips += 1 + +        # Print statistics every 5 seconds. +        time_delta = time - self.time_last +        if time_delta >= 5: +            frame_delta = frame - self.frame_last +            flips_delta = self.flips - self.flips_last +            print("Frame rate: %f (%u/%u frames in %f s)" % +                  (frame_delta / time_delta, flips_delta, frame_delta, time_delta)) + +            self.frame_last = frame +            self.flips_last = self.flips +            self.time_last = time + +        # Draw the color bar on the back buffer. +        if self.front_buf == 0: +            fb = self.fb2 +        else: +            fb = self.fb1 + +        self.front_buf = self.front_buf ^ 1 + +        current_xpos = self.bar_xpos; +        old_xpos = (current_xpos + (fb.width - bar_width - bar_speed)) % (fb.width - bar_width); +        new_xpos = (current_xpos + bar_speed) % (fb.width - bar_width); + +        self.bar_xpos = new_xpos + +        pykms.draw_color_bar(fb, old_xpos, new_xpos, bar_width) + +        # Flip the buffers with an in fence located in the future. The atomic +        # commit is asynchronous and returns immediately, but the flip should +        # not complete before the fence gets signaled. +        print("flipping with fence @%u, timeline is @%u" % (2 * self.flips - 1, self.timeline.value)) +        fence = self.timeline.create_fence(2 * self.flips - 1) +        req = pykms.AtomicReq(self.crtc.card) +        req.add(self.crtc.primary_plane, { 'FB_ID': fb.id, 'IN_FENCE_FD': fence.fd }) +        req.commit(self) +        del fence + +        # Arm a timer to signal the fence in 0.5s. +        def timeline_signal(timeline): +            print("signaling timeline @%u" % timeline.value) +            timeline.signal(2) + +        Timer(0.5, timeline_signal, self.timeline) + + +def main(argv): +    if len(argv) > 1: +        conn_name = argv[1] +    else: +        conn_name = '' + +    card = pykms.Card() +    if not card.has_atomic: +        raise RuntimeError('This test requires atomic update support') + +    res = pykms.ResourceManager(card) +    conn = res.reserve_connector(conn_name) +    crtc = res.reserve_crtc(conn) +    mode = conn.get_default_mode() + +    flip_handler = FlipHandler(crtc, mode.hdisplay, mode.vdisplay) + +    fb = flip_handler.fb1 +    pykms.draw_color_bar(fb, fb.width - bar_width - bar_speed, bar_speed, bar_width) +    mode_blob = mode.blob(card) + +    req = pykms.AtomicReq(card) +    req.add(conn, 'CRTC_ID', crtc.id) +    req.add(crtc, { 'ACTIVE': 1, 'MODE_ID': mode_blob.id }) +    req.add(crtc.primary_plane, { +                'FB_ID': fb.id, +                'CRTC_ID': crtc.id, +                'SRC_X': 0 << 16, +                'SRC_Y': 0 << 16, +                'SRC_W': fb.width << 16, +                'SRC_H': fb.height << 16, +                'CRTC_X': 0, +                'CRTC_Y': 0, +                'CRTC_W': fb.width, +                'CRTC_H': fb.height, +    }) +    ret = req.commit(flip_handler, allow_modeset = True) +    if ret < 0: +        raise RuntimeError('Atomic mode set failed with %d' % ret) + +    def readdrm(fileobj, mask): +        for ev in card.read_events(): +            if ev.type == pykms.DrmEventType.FLIP_COMPLETE: +                ev.data.handle_page_flip(ev.seq, ev.time) + +    def readkey(fileobj, mask): +        # Signal the timeline to complete all pending page flips +        flip_handler.timeline. signal(100) +        sys.stdin.readline() +        exit(0) + +    sel = selectors.DefaultSelector() +    sel.register(card.fd, selectors.EVENT_READ, readdrm) +    sel.register(sys.stdin, selectors.EVENT_READ, readkey) + +    while True: +        timeout = Timer.next_timeout() +        print("--> timeout %s" % repr(timeout)) +        events = sel.select(timeout) +        for key, mask in events: +            callback = key.data +            callback(key.fileobj, mask) + +        Timer.fire() + +if __name__ == '__main__': +    main(sys.argv) | 
