py: tests: Double buffered one big buffer on all connected screens
authorPeter Ujfalusi <peter.ujfalusi@ti.com>
Fri, 7 Apr 2017 10:29:26 +0000 (13:29 +0300)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Tue, 9 May 2017 08:26:00 +0000 (11:26 +0300)
The test uses Atomic Mode Setting only and moves one horizontal bar
up/down in the framebuffer - which stretches through all connected
displays.

The flip mode can be selected with --flipmode <single, separate> :
single: Page flip on all displays with one request (default)
separate: Separate page flip on the displays

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
py/tests/big_fb.py [new file with mode: 0755]

diff --git a/py/tests/big_fb.py b/py/tests/big_fb.py
new file mode 100755 (executable)
index 0000000..1642376
--- /dev/null
@@ -0,0 +1,288 @@
+#!/usr/bin/python3
+
+import pykms
+import random
+import time
+import sys
+import select
+import argparse
+import selectors
+
+black = pykms.RGB(0, 0, 0)
+
+parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
+parser.add_argument('--flipmode', choices=['single', 'separate'], default='single', required=False,
+    help="""Page flip method to use:
+    single: Page flip on all displays with one request (default)
+    separate: Separate page flip on the displays""")
+
+args = parser.parse_args()
+
+card = pykms.Card()
+
+if not card.has_atomic:
+    print('Atomic mode settings is not supported :(')
+    sys.exit()
+
+if args.flipmode == 'single':
+    print('Page flip on all displays with one request')
+elif args.flipmode == 'separate':
+    print('Page flip on all displays with separate requests')
+
+res = pykms.ResourceManager(card)
+
+conn_list = []
+crtc_list = []
+mode_list = []
+plane_list = []
+big_fb_list = []
+
+for conn in card.connectors:
+    if conn.connected() == 1:
+        conn_list.append(conn)
+
+print('Have {} connected connectors:'.format(len(conn_list)))
+for conn in conn_list:
+    crtc = res.reserve_crtc(conn)
+    crtc_list.append(crtc)
+
+    mode = conn.get_default_mode()
+    mode_list.append(mode)
+
+    print(' {}: {} ({}x{})'.format(conn.idx, conn.fullname,
+        mode.hdisplay, mode.vdisplay))
+
+fbX = sum(mode.hdisplay for mode in mode_list)
+fbY = max(mode.vdisplay for mode in mode_list)
+
+print('FB Resolution: {}x{}\n'.format(fbX, fbY))
+
+# Create the (big)framebuffer(s)
+for x in range(2):
+    fb_tmp = pykms.DumbFramebuffer(card, fbX, fbY, 'XR24');
+    big_fb_list.append(fb_tmp)
+
+fb = big_fb_list[0]
+screen_offset = 0
+
+card.disable_planes()
+for i in range(0, len(conn_list)):
+    conn = conn_list[i]
+    crtc = crtc_list[i]
+    mode = mode_list[i]
+
+    plane = res.reserve_generic_plane(crtc)
+    plane_list.append(plane)
+
+    modeb = mode.to_blob(card)
+    req = pykms.AtomicReq(card)
+    req.add(conn, 'CRTC_ID', crtc.id)
+    req.add(crtc, {'ACTIVE': 1,
+                    'MODE_ID': modeb.id})
+    req.add(plane, {'FB_ID': fb.id,
+                    'CRTC_ID': crtc.id,
+                    'SRC_X': screen_offset << 16,
+                    'SRC_Y': 0 << 16,
+                    'SRC_W': mode.hdisplay << 16,
+                    'SRC_H': mode.vdisplay << 16,
+                    'CRTC_X': 0,
+                    'CRTC_Y': 0,
+                    'CRTC_W': mode.hdisplay,
+                    'CRTC_H': mode.vdisplay,
+                    'zorder': 0})
+
+    req.commit_sync(allow_modeset = True)
+
+    screen_offset += mode.hdisplay
+
+# Double buffering, page flipping
+class bigFB_db:
+    def __init__(self, fb1, fb2):
+        self.speed_y = random.randrange(1, 10, 1)
+        self.dir_y = random.randrange(-1, 3, 2)
+        self.first_run = True
+        self.fbs = [fb1,fb2]
+        self.draw_buf = 0
+        self.fbX = fb1.width
+        self.fbY = fb1.height
+        self.pos_y = self.fbY // 2
+        self.old_pos_y = -1
+        # 5 + 10 + 15 + 10 + 5 = 45
+        self.bar_size = 45
+        self.flips = 0
+        self.frames = 0
+        self.time = 0
+
+    def new_color(self):
+        r = random.randrange(255)
+        g = random.randrange(255)
+        b = random.randrange(255)
+        self.color = pykms.RGB(r, g, b)
+        self.color2 = pykms.RGB(r // 2, g // 2, b // 2)
+        self.color3 = pykms.RGB(r // 3, g // 3, b // 3)
+    def move_stripe(self):
+        if self.first_run:
+            self.new_color()
+            self.first_run = False
+
+        fb = self.fbs[self.draw_buf]
+
+        old_box_y = self.old_pos_y
+        self.old_pos_y = self.pos_y
+        change_speed = 0
+
+        self.pos_y = int(self.pos_y + (self.dir_y * self.speed_y))
+
+        if self.pos_y < 0:
+            self.pos_y = 0
+            change_speed = 1
+            self.dir_y = 1
+        elif self.pos_y > (self.fbY - self.bar_size):
+            self.pos_y = self.fbY - self.bar_size
+            change_speed = 1
+            self.dir_y = -1
+
+        if change_speed == 1:
+            self.new_color()
+            self.speed_y = random.randrange(1, 10, 1)
+
+        # Erease the old box
+        if old_box_y >= 0:
+            pykms.draw_rect(fb, 0, old_box_y, self.fbX, self.bar_size, black)
+
+        pos_y = self.pos_y
+        pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3)
+        pos_y += 5
+        pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2)
+        pos_y += 10
+        pykms.draw_rect(fb, 0, pos_y, self.fbX, 15, self.color)
+        pos_y += 15
+        pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2)
+        pos_y += 10
+        pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3)
+
+    def handle_page_flip_single(self):
+        self.draw_buf ^= 1
+        self.move_stripe()
+
+        # one atomic request to flip on all displays/crtcs
+        fb = self.fbs[self.draw_buf]
+        screen_offset = 0
+
+        req = pykms.AtomicReq(card)
+        for i in range(0, len(conn_list)):
+            crtc = crtc_list[i]
+            mode = mode_list[i]
+
+            plane = plane_list[i]
+
+            req.add(plane, {'FB_ID': fb.id,
+                            'CRTC_ID': crtc.id,
+                            'SRC_X': screen_offset << 16,
+                            'SRC_Y': 0 << 16,
+                            'SRC_W': mode.hdisplay << 16,
+                            'SRC_H': mode.vdisplay << 16,
+                            'CRTC_X': 0,
+                            'CRTC_Y': 0,
+                            'CRTC_W': mode.hdisplay,
+                            'CRTC_H': mode.vdisplay,
+                            'zorder': 0})
+
+            screen_offset += mode.hdisplay
+
+        req.commit(self)
+
+    def handle_page_flip_separate(self):
+        self.draw_buf ^= 1
+        self.move_stripe()
+
+        # ask to flip the first screen
+        fb = self.fbs[self.draw_buf]
+        screen_offset = 0
+
+        # add separate atomic request for each display (crtc)
+        for i in range(0, len(conn_list)):
+            req = pykms.AtomicReq(card)
+            crtc = crtc_list[i]
+            mode = mode_list[i]
+
+            plane = plane_list[i]
+
+            req.add(plane, {'FB_ID': fb.id,
+                            'CRTC_ID': crtc.id,
+                            'SRC_X': screen_offset << 16,
+                            'SRC_Y': 0 << 16,
+                            'SRC_W': mode.hdisplay << 16,
+                            'SRC_H': mode.vdisplay << 16,
+                            'CRTC_X': 0,
+                            'CRTC_Y': 0,
+                            'CRTC_W': mode.hdisplay,
+                            'CRTC_H': mode.vdisplay,
+                            'zorder': 0})
+
+            screen_offset += mode.hdisplay
+
+            req.commit(self)
+
+    def handle_page_flip_main(self, frame, time):
+        # statistics
+        self.flips += 1
+        if self.time == 0:
+            self.frames = frame
+            self.time = time
+
+        time_delta = time - self.time
+        if time_delta >= 5:
+            frame_delta = frame - self.frames
+            print('Frame rate: %f (%u/%u frames in %f s)' %
+                  (frame_delta / time_delta, self.flips, frame_delta, time_delta))
+
+            self.flips = 0
+            self.frames = frame
+            self.time = time
+
+        if args.flipmode == 'single':
+            self.handle_page_flip_single()
+        elif args.flipmode == 'separate':
+            self.handle_page_flip_separate()
+
+print('Press ENTER to exit\n')
+
+box_db = bigFB_db(big_fb_list[0], big_fb_list[1])
+box_db.handle_page_flip_main(0, 0)
+
+def readdrm(fileobj, mask):
+    for ev in card.read_events():
+        if ev.type == pykms.DrmEventType.FLIP_COMPLETE:
+            ev.data.handle_page_flip_main(ev.seq, ev.time)
+
+event_counter = len(conn_list)
+def readdrm_counted(fileobj, mask):
+    global event_counter
+
+    for ev in card.read_events():
+        if ev.type == pykms.DrmEventType.FLIP_COMPLETE:
+            # we expect events for each display (crtc), but only execute the
+            # next drawing and flip when we have received the last event.
+            event_counter -= 1
+            if event_counter == 0:
+                event_counter = len(conn_list)
+                ev.data.handle_page_flip_main(ev.seq, ev.time)
+
+def readkey(fileobj, mask):
+    sys.stdin.readline()
+    exit(0)
+
+sel = selectors.DefaultSelector()
+if args.flipmode == 'single':
+    sel.register(card.fd, selectors.EVENT_READ, readdrm)
+else:
+    sel.register(card.fd, selectors.EVENT_READ, readdrm_counted)
+
+sel.register(sys.stdin, selectors.EVENT_READ, readkey)
+
+while True:
+    events = sel.select()
+    for key, mask in events:
+        callback = key.data
+        callback(key.fileobj, mask)