1#!/usr/bin/python3 2 3import pykms 4import random 5import time 6import sys 7import select 8import argparse 9import selectors 10 11black = pykms.RGB(0, 0, 0) 12 13parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) 14parser.add_argument('--flipmode', choices=['single', 'separate'], default='single', required=False, 15 help="""Page flip method to use: 16 single: Page flip on all displays with one request (default) 17 separate: Separate page flip on the displays""") 18 19args = parser.parse_args() 20 21card = pykms.Card() 22 23if not card.has_atomic: 24 print('Atomic mode settings is not supported :(') 25 sys.exit() 26 27if args.flipmode == 'single': 28 print('Page flip on all displays with one request') 29elif args.flipmode == 'separate': 30 print('Page flip on all displays with separate requests') 31 32res = pykms.ResourceManager(card) 33 34conn_list = [] 35crtc_list = [] 36mode_list = [] 37plane_list = [] 38big_fb_list = [] 39 40for conn in card.connectors: 41 if conn.connected() == 1: 42 conn_list.append(conn) 43 44print('Have {} connected connectors:'.format(len(conn_list))) 45for conn in conn_list: 46 crtc = res.reserve_crtc(conn) 47 crtc_list.append(crtc) 48 49 mode = conn.get_default_mode() 50 mode_list.append(mode) 51 52 print(' {}: {} ({}x{})'.format(conn.idx, conn.fullname, 53 mode.hdisplay, mode.vdisplay)) 54 55fbX = sum(mode.hdisplay for mode in mode_list) 56fbY = max(mode.vdisplay for mode in mode_list) 57 58print('FB Resolution: {}x{}\n'.format(fbX, fbY)) 59 60# Create the (big)framebuffer(s) 61for x in range(2): 62 fb_tmp = pykms.DumbFramebuffer(card, fbX, fbY, 'XR24'); 63 big_fb_list.append(fb_tmp) 64 65fb = big_fb_list[0] 66screen_offset = 0 67 68card.disable_planes() 69for i in range(0, len(conn_list)): 70 conn = conn_list[i] 71 crtc = crtc_list[i] 72 mode = mode_list[i] 73 74 plane = res.reserve_generic_plane(crtc) 75 plane_list.append(plane) 76 77 modeb = mode.to_blob(card) 78 req = pykms.AtomicReq(card) 79 req.add(conn, 'CRTC_ID', crtc.id) 80 req.add(crtc, {'ACTIVE': 1, 81 'MODE_ID': modeb.id}) 82 req.add(plane, {'FB_ID': fb.id, 83 'CRTC_ID': crtc.id, 84 'SRC_X': screen_offset << 16, 85 'SRC_Y': 0 << 16, 86 'SRC_W': mode.hdisplay << 16, 87 'SRC_H': mode.vdisplay << 16, 88 'CRTC_X': 0, 89 'CRTC_Y': 0, 90 'CRTC_W': mode.hdisplay, 91 'CRTC_H': mode.vdisplay, 92 'zorder': 0}) 93 94 req.commit_sync(allow_modeset = True) 95 96 screen_offset += mode.hdisplay 97 98# Double buffering, page flipping 99class bigFB_db: 100 def __init__(self, fb1, fb2): 101 self.speed_y = random.randrange(1, 10, 1) 102 self.dir_y = random.randrange(-1, 3, 2) 103 self.first_run = True 104 self.fbs = [fb1,fb2] 105 self.draw_buf = 0 106 self.fbX = fb1.width 107 self.fbY = fb1.height 108 self.pos_y = self.fbY // 2 109 self.old_pos_y = -1 110 # 5 + 10 + 15 + 10 + 5 = 45 111 self.bar_size = 45 112 self.flips = 0 113 self.frames = 0 114 self.time = 0 115 self.flip_count = 100 116 117 def new_color(self): 118 r = random.randrange(255) 119 g = random.randrange(255) 120 b = random.randrange(255) 121 self.color = pykms.RGB(r, g, b) 122 self.color2 = pykms.RGB(r // 2, g // 2, b // 2) 123 self.color3 = pykms.RGB(r // 3, g // 3, b // 3) 124 def move_stripe(self): 125 if self.first_run: 126 self.new_color() 127 self.first_run = False 128 129 fb = self.fbs[self.draw_buf] 130 131 old_box_y = self.old_pos_y 132 self.old_pos_y = self.pos_y 133 change_speed = 0 134 135 self.pos_y = int(self.pos_y + (self.dir_y * self.speed_y)) 136 137 if self.pos_y < 0: 138 self.pos_y = 0 139 change_speed = 1 140 self.dir_y = 1 141 elif self.pos_y > (self.fbY - self.bar_size): 142 self.pos_y = self.fbY - self.bar_size 143 change_speed = 1 144 self.dir_y = -1 145 146 if change_speed == 1: 147 self.new_color() 148 self.speed_y = random.randrange(1, 10, 1) 149 150 # Erease the old box 151 if old_box_y >= 0: 152 pykms.draw_rect(fb, 0, old_box_y, self.fbX, self.bar_size, black) 153 154 pos_y = self.pos_y 155 pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3) 156 pos_y += 5 157 pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2) 158 pos_y += 10 159 pykms.draw_rect(fb, 0, pos_y, self.fbX, 15, self.color) 160 pos_y += 15 161 pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2) 162 pos_y += 10 163 pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3) 164 165 def handle_page_flip_single(self): 166 self.draw_buf ^= 1 167 self.move_stripe() 168 169 # one atomic request to flip on all displays/crtcs 170 fb = self.fbs[self.draw_buf] 171 screen_offset = 0 172 173 req = pykms.AtomicReq(card) 174 for i in range(0, len(conn_list)): 175 crtc = crtc_list[i] 176 mode = mode_list[i] 177 178 plane = plane_list[i] 179 180 req.add(plane, {'FB_ID': fb.id, 181 'CRTC_ID': crtc.id, 182 'SRC_X': screen_offset << 16, 183 'SRC_Y': 0 << 16, 184 'SRC_W': mode.hdisplay << 16, 185 'SRC_H': mode.vdisplay << 16, 186 'CRTC_X': 0, 187 'CRTC_Y': 0, 188 'CRTC_W': mode.hdisplay, 189 'CRTC_H': mode.vdisplay, 190 'zorder': 0}) 191 192 screen_offset += mode.hdisplay 193 194 req.commit(0) 195 196 def handle_page_flip_separate(self): 197 self.draw_buf ^= 1 198 self.move_stripe() 199 200 # ask to flip the first screen 201 fb = self.fbs[self.draw_buf] 202 screen_offset = 0 203 204 # add separate atomic request for each display (crtc) 205 for i in range(0, len(conn_list)): 206 req = pykms.AtomicReq(card) 207 crtc = crtc_list[i] 208 mode = mode_list[i] 209 210 plane = plane_list[i] 211 212 req.add(plane, {'FB_ID': fb.id, 213 'CRTC_ID': crtc.id, 214 'SRC_X': screen_offset << 16, 215 'SRC_Y': 0 << 16, 216 'SRC_W': mode.hdisplay << 16, 217 'SRC_H': mode.vdisplay << 16, 218 'CRTC_X': 0, 219 'CRTC_Y': 0, 220 'CRTC_W': mode.hdisplay, 221 'CRTC_H': mode.vdisplay, 222 'zorder': 0}) 223 224 screen_offset += mode.hdisplay 225 226 req.commit(0) 227 228 def handle_page_flip_main(self, frame, time): 229 self.flip_count += 1 230 231 if self.flip_count < len(conn_list): 232 return 233 234 self.flip_count = 0 235 236 # statistics 237 self.flips += 1 238 if self.time == 0: 239 self.frames = frame 240 self.time = time 241 242 time_delta = time - self.time 243 if time_delta >= 5: 244 frame_delta = frame - self.frames 245 print('Frame rate: %f (%u/%u frames in %f s)' % 246 (frame_delta / time_delta, self.flips, frame_delta, time_delta)) 247 248 self.flips = 0 249 self.frames = frame 250 self.time = time 251 252 if args.flipmode == 'single': 253 self.handle_page_flip_single() 254 elif args.flipmode == 'separate': 255 self.handle_page_flip_separate() 256 257print('Press ENTER to exit\n') 258 259box_db = bigFB_db(big_fb_list[0], big_fb_list[1]) 260box_db.handle_page_flip_main(0, 0) 261 262def readdrm(fileobj, mask): 263 for ev in card.read_events(): 264 if ev.type == pykms.DrmEventType.FLIP_COMPLETE: 265 box_db.handle_page_flip_main(ev.seq, ev.time) 266 267def readkey(fileobj, mask): 268 sys.stdin.readline() 269 exit(0) 270 271sel = selectors.DefaultSelector() 272sel.register(card.fd, selectors.EVENT_READ, readdrm) 273sel.register(sys.stdin, selectors.EVENT_READ, readkey) 274 275while True: 276 events = sel.select() 277 for key, mask in events: 278 callback = key.data 279 callback(key.fileobj, mask) 280