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