1 #!/usr/bin/python3
3 import pykms
4 import random
5 import time
6 import sys
7 import select
8 import argparse
9 import selectors
11 black = pykms.RGB(0, 0, 0)
13 parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
14 parser.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""")
19 args = parser.parse_args()
21 card = pykms.Card()
23 if not card.has_atomic:
24 print('Atomic mode settings is not supported :(')
25 sys.exit()
27 if args.flipmode == 'single':
28 print('Page flip on all displays with one request')
29 elif args.flipmode == 'separate':
30 print('Page flip on all displays with separate requests')
32 res = pykms.ResourceManager(card)
34 conn_list = []
35 crtc_list = []
36 mode_list = []
37 plane_list = []
38 big_fb_list = []
40 for conn in card.connectors:
41 if conn.connected() == 1:
42 conn_list.append(conn)
44 print('Have {} connected connectors:'.format(len(conn_list)))
45 for conn in conn_list:
46 crtc = res.reserve_crtc(conn)
47 crtc_list.append(crtc)
49 mode = conn.get_default_mode()
50 mode_list.append(mode)
52 print(' {}: {} ({}x{})'.format(conn.idx, conn.fullname,
53 mode.hdisplay, mode.vdisplay))
55 fbX = sum(mode.hdisplay for mode in mode_list)
56 fbY = max(mode.vdisplay for mode in mode_list)
58 print('FB Resolution: {}x{}\n'.format(fbX, fbY))
60 # Create the (big)framebuffer(s)
61 for x in range(2):
62 fb_tmp = pykms.DumbFramebuffer(card, fbX, fbY, 'XR24');
63 big_fb_list.append(fb_tmp)
65 fb = big_fb_list[0]
66 screen_offset = 0
68 card.disable_planes()
69 for i in range(0, len(conn_list)):
70 conn = conn_list[i]
71 crtc = crtc_list[i]
72 mode = mode_list[i]
74 plane = res.reserve_generic_plane(crtc)
75 plane_list.append(plane)
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})
94 req.commit_sync(allow_modeset = True)
96 screen_offset += mode.hdisplay
98 # Double buffering, page flipping
99 class 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
116 def new_color(self):
117 r = random.randrange(255)
118 g = random.randrange(255)
119 b = random.randrange(255)
120 self.color = pykms.RGB(r, g, b)
121 self.color2 = pykms.RGB(r // 2, g // 2, b // 2)
122 self.color3 = pykms.RGB(r // 3, g // 3, b // 3)
123 def move_stripe(self):
124 if self.first_run:
125 self.new_color()
126 self.first_run = False
128 fb = self.fbs[self.draw_buf]
130 old_box_y = self.old_pos_y
131 self.old_pos_y = self.pos_y
132 change_speed = 0
134 self.pos_y = int(self.pos_y + (self.dir_y * self.speed_y))
136 if self.pos_y < 0:
137 self.pos_y = 0
138 change_speed = 1
139 self.dir_y = 1
140 elif self.pos_y > (self.fbY - self.bar_size):
141 self.pos_y = self.fbY - self.bar_size
142 change_speed = 1
143 self.dir_y = -1
145 if change_speed == 1:
146 self.new_color()
147 self.speed_y = random.randrange(1, 10, 1)
149 # Erease the old box
150 if old_box_y >= 0:
151 pykms.draw_rect(fb, 0, old_box_y, self.fbX, self.bar_size, black)
153 pos_y = self.pos_y
154 pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3)
155 pos_y += 5
156 pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2)
157 pos_y += 10
158 pykms.draw_rect(fb, 0, pos_y, self.fbX, 15, self.color)
159 pos_y += 15
160 pykms.draw_rect(fb, 0, pos_y, self.fbX, 10, self.color2)
161 pos_y += 10
162 pykms.draw_rect(fb, 0, pos_y, self.fbX, 5, self.color3)
164 def handle_page_flip_single(self):
165 self.draw_buf ^= 1
166 self.move_stripe()
168 # one atomic request to flip on all displays/crtcs
169 fb = self.fbs[self.draw_buf]
170 screen_offset = 0
172 req = pykms.AtomicReq(card)
173 for i in range(0, len(conn_list)):
174 crtc = crtc_list[i]
175 mode = mode_list[i]
177 plane = plane_list[i]
179 req.add(plane, {'FB_ID': fb.id,
180 'CRTC_ID': crtc.id,
181 'SRC_X': screen_offset << 16,
182 'SRC_Y': 0 << 16,
183 'SRC_W': mode.hdisplay << 16,
184 'SRC_H': mode.vdisplay << 16,
185 'CRTC_X': 0,
186 'CRTC_Y': 0,
187 'CRTC_W': mode.hdisplay,
188 'CRTC_H': mode.vdisplay,
189 'zorder': 0})
191 screen_offset += mode.hdisplay
193 req.commit(self)
195 def handle_page_flip_separate(self):
196 self.draw_buf ^= 1
197 self.move_stripe()
199 # ask to flip the first screen
200 fb = self.fbs[self.draw_buf]
201 screen_offset = 0
203 # add separate atomic request for each display (crtc)
204 for i in range(0, len(conn_list)):
205 req = pykms.AtomicReq(card)
206 crtc = crtc_list[i]
207 mode = mode_list[i]
209 plane = plane_list[i]
211 req.add(plane, {'FB_ID': fb.id,
212 'CRTC_ID': crtc.id,
213 'SRC_X': screen_offset << 16,
214 'SRC_Y': 0 << 16,
215 'SRC_W': mode.hdisplay << 16,
216 'SRC_H': mode.vdisplay << 16,
217 'CRTC_X': 0,
218 'CRTC_Y': 0,
219 'CRTC_W': mode.hdisplay,
220 'CRTC_H': mode.vdisplay,
221 'zorder': 0})
223 screen_offset += mode.hdisplay
225 req.commit(self)
227 def handle_page_flip_main(self, frame, time):
228 # statistics
229 self.flips += 1
230 if self.time == 0:
231 self.frames = frame
232 self.time = time
234 time_delta = time - self.time
235 if time_delta >= 5:
236 frame_delta = frame - self.frames
237 print('Frame rate: %f (%u/%u frames in %f s)' %
238 (frame_delta / time_delta, self.flips, frame_delta, time_delta))
240 self.flips = 0
241 self.frames = frame
242 self.time = time
244 if args.flipmode == 'single':
245 self.handle_page_flip_single()
246 elif args.flipmode == 'separate':
247 self.handle_page_flip_separate()
249 print('Press ENTER to exit\n')
251 box_db = bigFB_db(big_fb_list[0], big_fb_list[1])
252 box_db.handle_page_flip_main(0, 0)
254 def readdrm(fileobj, mask):
255 for ev in card.read_events():
256 if ev.type == pykms.DrmEventType.FLIP_COMPLETE:
257 ev.data.handle_page_flip_main(ev.seq, ev.time)
259 event_counter = len(conn_list)
260 def readdrm_counted(fileobj, mask):
261 global event_counter
263 for ev in card.read_events():
264 if ev.type == pykms.DrmEventType.FLIP_COMPLETE:
265 # we expect events for each display (crtc), but only execute the
266 # next drawing and flip when we have received the last event.
267 event_counter -= 1
268 if event_counter == 0:
269 event_counter = len(conn_list)
270 ev.data.handle_page_flip_main(ev.seq, ev.time)
272 def readkey(fileobj, mask):
273 sys.stdin.readline()
274 exit(0)
276 sel = selectors.DefaultSelector()
277 if args.flipmode == 'single':
278 sel.register(card.fd, selectors.EVENT_READ, readdrm)
279 else:
280 sel.register(card.fd, selectors.EVENT_READ, readdrm_counted)
282 sel.register(sys.stdin, selectors.EVENT_READ, readkey)
284 while True:
285 events = sel.select()
286 for key, mask in events:
287 callback = key.data
288 callback(key.fileobj, mask)