modeset-vsync.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. /*
  2. * modeset - DRM Double-Buffered VSync'ed Modesetting Example
  3. *
  4. * Written 2012 by David Rheinsberg <david.rheinsberg@gmail.com>
  5. * Dedicated to the Public Domain.
  6. */
  7. /*
  8. * DRM Double-Buffered VSync'ed Modesetting Howto
  9. * This example extends modeset-double-buffered.c and introduces page-flips
  10. * synced with vertical-blanks (vsync'ed). A vertical-blank is the time-period
  11. * when a display-controller pauses from scanning out the framebuffer. After the
  12. * vertical-blank is over, the framebuffer is again scanned out line by line and
  13. * followed again by a vertical-blank.
  14. *
  15. * Vertical-blanks are important when changing a framebuffer. We already
  16. * introduced double-buffering, so this example shows how we can flip the
  17. * buffers during a vertical blank and _not_ during the scanout period.
  18. *
  19. * This example assumes that you are familiar with modeset-double-buffered. Only
  20. * the differences between both files are highlighted here.
  21. */
  22. #define _GNU_SOURCE
  23. #include <errno.h>
  24. #include <fcntl.h>
  25. #include <stdbool.h>
  26. #include <stdint.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <sys/mman.h>
  31. #include <time.h>
  32. #include <unistd.h>
  33. #include <xf86drm.h>
  34. #include <xf86drmMode.h>
  35. struct modeset_buf;
  36. struct modeset_dev;
  37. static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
  38. struct modeset_dev *dev);
  39. static int modeset_create_fb(int fd, struct modeset_buf *buf);
  40. static void modeset_destroy_fb(int fd, struct modeset_buf *buf);
  41. static int modeset_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn,
  42. struct modeset_dev *dev);
  43. static int modeset_open(int *out, const char *node);
  44. static int modeset_prepare(int fd);
  45. static void modeset_draw(int fd);
  46. static void modeset_draw_dev(int fd, struct modeset_dev *dev);
  47. static void modeset_cleanup(int fd);
  48. /*
  49. * modeset_open() stays the same.
  50. */
  51. static int modeset_open(int *out, const char *node)
  52. {
  53. int fd, ret;
  54. uint64_t has_dumb;
  55. fd = open(node, O_RDWR | O_CLOEXEC);
  56. if (fd < 0) {
  57. ret = -errno;
  58. fprintf(stderr, "cannot open '%s': %m\n", node);
  59. return ret;
  60. }
  61. if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
  62. !has_dumb) {
  63. fprintf(stderr, "drm device '%s' does not support dumb buffers\n",
  64. node);
  65. close(fd);
  66. return -EOPNOTSUPP;
  67. }
  68. *out = fd;
  69. return 0;
  70. }
  71. /*
  72. * modeset_buf and modeset_dev stay mostly the same. But 6 new fields are added
  73. * to modeset_dev: r, g, b, r_up, g_up, b_up. They are used to compute the
  74. * current color that is drawn on this output device. You can ignore them as
  75. * they aren't important for this example.
  76. * The modeset-double-buffered.c example used exactly the same fields but as
  77. * local variables in modeset_draw().
  78. *
  79. * The \pflip_pending variable is true when a page-flip is currently pending,
  80. * that is, the kernel will flip buffers on the next vertical blank. The
  81. * \cleanup variable is true if the device is currently cleaned up and no more
  82. * pageflips should be scheduled. They are used to synchronize the cleanup
  83. * routines.
  84. */
  85. struct modeset_buf {
  86. uint32_t width;
  87. uint32_t height;
  88. uint32_t stride;
  89. uint32_t size;
  90. uint32_t handle;
  91. uint8_t *map;
  92. uint32_t fb;
  93. };
  94. struct modeset_dev {
  95. struct modeset_dev *next;
  96. unsigned int front_buf;
  97. struct modeset_buf bufs[2];
  98. drmModeModeInfo mode;
  99. uint32_t conn;
  100. uint32_t crtc;
  101. drmModeCrtc *saved_crtc;
  102. bool pflip_pending;
  103. bool cleanup;
  104. uint8_t r, g, b;
  105. bool r_up, g_up, b_up;
  106. };
  107. static struct modeset_dev *modeset_list = NULL;
  108. /*
  109. * modeset_prepare() stays the same.
  110. */
  111. static int modeset_prepare(int fd)
  112. {
  113. drmModeRes *res;
  114. drmModeConnector *conn;
  115. unsigned int i;
  116. struct modeset_dev *dev;
  117. int ret;
  118. /* retrieve resources */
  119. res = drmModeGetResources(fd);
  120. if (!res) {
  121. fprintf(stderr, "cannot retrieve DRM resources (%d): %m\n",
  122. errno);
  123. return -errno;
  124. }
  125. /* iterate all connectors */
  126. for (i = 0; i < res->count_connectors; ++i) {
  127. /* get information for each connector */
  128. conn = drmModeGetConnector(fd, res->connectors[i]);
  129. if (!conn) {
  130. fprintf(stderr, "cannot retrieve DRM connector %u:%u (%d): %m\n",
  131. i, res->connectors[i], errno);
  132. continue;
  133. }
  134. /* create a device structure */
  135. dev = malloc(sizeof(*dev));
  136. memset(dev, 0, sizeof(*dev));
  137. dev->conn = conn->connector_id;
  138. /* call helper function to prepare this connector */
  139. ret = modeset_setup_dev(fd, res, conn, dev);
  140. if (ret) {
  141. if (ret != -ENOENT) {
  142. errno = -ret;
  143. fprintf(stderr, "cannot setup device for connector %u:%u (%d): %m\n",
  144. i, res->connectors[i], errno);
  145. }
  146. free(dev);
  147. drmModeFreeConnector(conn);
  148. continue;
  149. }
  150. /* free connector data and link device into global list */
  151. drmModeFreeConnector(conn);
  152. dev->next = modeset_list;
  153. modeset_list = dev;
  154. }
  155. /* free resources again */
  156. drmModeFreeResources(res);
  157. return 0;
  158. }
  159. /*
  160. * modeset_setup_dev() stays the same.
  161. */
  162. static int modeset_setup_dev(int fd, drmModeRes *res, drmModeConnector *conn,
  163. struct modeset_dev *dev)
  164. {
  165. int ret;
  166. /* check if a monitor is connected */
  167. if (conn->connection != DRM_MODE_CONNECTED) {
  168. fprintf(stderr, "ignoring unused connector %u\n",
  169. conn->connector_id);
  170. return -ENOENT;
  171. }
  172. /* check if there is at least one valid mode */
  173. if (conn->count_modes == 0) {
  174. fprintf(stderr, "no valid mode for connector %u\n",
  175. conn->connector_id);
  176. return -EFAULT;
  177. }
  178. /* copy the mode information into our device structure and into both
  179. * buffers */
  180. memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode));
  181. dev->bufs[0].width = conn->modes[0].hdisplay;
  182. dev->bufs[0].height = conn->modes[0].vdisplay;
  183. dev->bufs[1].width = conn->modes[0].hdisplay;
  184. dev->bufs[1].height = conn->modes[0].vdisplay;
  185. fprintf(stderr, "mode for connector %u is %ux%u\n",
  186. conn->connector_id, dev->bufs[0].width, dev->bufs[0].height);
  187. /* find a crtc for this connector */
  188. ret = modeset_find_crtc(fd, res, conn, dev);
  189. if (ret) {
  190. fprintf(stderr, "no valid crtc for connector %u\n",
  191. conn->connector_id);
  192. return ret;
  193. }
  194. /* create framebuffer #1 for this CRTC */
  195. ret = modeset_create_fb(fd, &dev->bufs[0]);
  196. if (ret) {
  197. fprintf(stderr, "cannot create framebuffer for connector %u\n",
  198. conn->connector_id);
  199. return ret;
  200. }
  201. /* create framebuffer #2 for this CRTC */
  202. ret = modeset_create_fb(fd, &dev->bufs[1]);
  203. if (ret) {
  204. fprintf(stderr, "cannot create framebuffer for connector %u\n",
  205. conn->connector_id);
  206. modeset_destroy_fb(fd, &dev->bufs[0]);
  207. return ret;
  208. }
  209. return 0;
  210. }
  211. /*
  212. * modeset_find_crtc() stays the same.
  213. */
  214. static int modeset_find_crtc(int fd, drmModeRes *res, drmModeConnector *conn,
  215. struct modeset_dev *dev)
  216. {
  217. drmModeEncoder *enc;
  218. unsigned int i, j;
  219. int32_t crtc;
  220. struct modeset_dev *iter;
  221. /* first try the currently conected encoder+crtc */
  222. if (conn->encoder_id)
  223. enc = drmModeGetEncoder(fd, conn->encoder_id);
  224. else
  225. enc = NULL;
  226. if (enc) {
  227. if (enc->crtc_id) {
  228. crtc = enc->crtc_id;
  229. for (iter = modeset_list; iter; iter = iter->next) {
  230. if (iter->crtc == crtc) {
  231. crtc = -1;
  232. break;
  233. }
  234. }
  235. if (crtc >= 0) {
  236. drmModeFreeEncoder(enc);
  237. dev->crtc = crtc;
  238. return 0;
  239. }
  240. }
  241. drmModeFreeEncoder(enc);
  242. }
  243. /* If the connector is not currently bound to an encoder or if the
  244. * encoder+crtc is already used by another connector (actually unlikely
  245. * but lets be safe), iterate all other available encoders to find a
  246. * matching CRTC. */
  247. for (i = 0; i < conn->count_encoders; ++i) {
  248. enc = drmModeGetEncoder(fd, conn->encoders[i]);
  249. if (!enc) {
  250. fprintf(stderr, "cannot retrieve encoder %u:%u (%d): %m\n",
  251. i, conn->encoders[i], errno);
  252. continue;
  253. }
  254. /* iterate all global CRTCs */
  255. for (j = 0; j < res->count_crtcs; ++j) {
  256. /* check whether this CRTC works with the encoder */
  257. if (!(enc->possible_crtcs & (1 << j)))
  258. continue;
  259. /* check that no other device already uses this CRTC */
  260. crtc = res->crtcs[j];
  261. for (iter = modeset_list; iter; iter = iter->next) {
  262. if (iter->crtc == crtc) {
  263. crtc = -1;
  264. break;
  265. }
  266. }
  267. /* we have found a CRTC, so save it and return */
  268. if (crtc >= 0) {
  269. drmModeFreeEncoder(enc);
  270. dev->crtc = crtc;
  271. return 0;
  272. }
  273. }
  274. drmModeFreeEncoder(enc);
  275. }
  276. fprintf(stderr, "cannot find suitable CRTC for connector %u\n",
  277. conn->connector_id);
  278. return -ENOENT;
  279. }
  280. /*
  281. * modeset_create_fb() stays the same.
  282. */
  283. static int modeset_create_fb(int fd, struct modeset_buf *buf)
  284. {
  285. struct drm_mode_create_dumb creq;
  286. struct drm_mode_destroy_dumb dreq;
  287. struct drm_mode_map_dumb mreq;
  288. int ret;
  289. /* create dumb buffer */
  290. memset(&creq, 0, sizeof(creq));
  291. creq.width = buf->width;
  292. creq.height = buf->height;
  293. creq.bpp = 32;
  294. ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
  295. if (ret < 0) {
  296. fprintf(stderr, "cannot create dumb buffer (%d): %m\n",
  297. errno);
  298. return -errno;
  299. }
  300. buf->stride = creq.pitch;
  301. buf->size = creq.size;
  302. buf->handle = creq.handle;
  303. /* create framebuffer object for the dumb-buffer */
  304. ret = drmModeAddFB(fd, buf->width, buf->height, 32, 32, buf->stride,
  305. buf->handle, &buf->fb);
  306. if (ret) {
  307. fprintf(stderr, "cannot create framebuffer (%d): %m\n",
  308. errno);
  309. ret = -errno;
  310. goto err_destroy;
  311. }
  312. /* prepare buffer for memory mapping */
  313. memset(&mreq, 0, sizeof(mreq));
  314. mreq.handle = buf->handle;
  315. ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
  316. if (ret) {
  317. fprintf(stderr, "cannot map dumb buffer (%d): %m\n",
  318. errno);
  319. ret = -errno;
  320. goto err_fb;
  321. }
  322. /* perform actual memory mapping */
  323. buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED,
  324. fd, mreq.offset);
  325. if (buf->map == MAP_FAILED) {
  326. fprintf(stderr, "cannot mmap dumb buffer (%d): %m\n",
  327. errno);
  328. ret = -errno;
  329. goto err_fb;
  330. }
  331. /* clear the framebuffer to 0 */
  332. memset(buf->map, 0, buf->size);
  333. return 0;
  334. err_fb:
  335. drmModeRmFB(fd, buf->fb);
  336. err_destroy:
  337. memset(&dreq, 0, sizeof(dreq));
  338. dreq.handle = buf->handle;
  339. drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
  340. return ret;
  341. }
  342. /*
  343. * modeset_destroy_fb() stays the same.
  344. */
  345. static void modeset_destroy_fb(int fd, struct modeset_buf *buf)
  346. {
  347. struct drm_mode_destroy_dumb dreq;
  348. /* unmap buffer */
  349. munmap(buf->map, buf->size);
  350. /* delete framebuffer */
  351. drmModeRmFB(fd, buf->fb);
  352. /* delete dumb buffer */
  353. memset(&dreq, 0, sizeof(dreq));
  354. dreq.handle = buf->handle;
  355. drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
  356. }
  357. /*
  358. * main() also stays the same.
  359. */
  360. int main(int argc, char **argv)
  361. {
  362. int ret, fd;
  363. const char *card;
  364. struct modeset_dev *iter;
  365. struct modeset_buf *buf;
  366. /* check which DRM device to open */
  367. if (argc > 1)
  368. card = argv[1];
  369. else
  370. card = "/dev/dri/card0";
  371. fprintf(stderr, "using card '%s'\n", card);
  372. /* open the DRM device */
  373. ret = modeset_open(&fd, card);
  374. if (ret)
  375. goto out_return;
  376. /* prepare all connectors and CRTCs */
  377. ret = modeset_prepare(fd);
  378. if (ret)
  379. goto out_close;
  380. /* perform actual modesetting on each found connector+CRTC */
  381. for (iter = modeset_list; iter; iter = iter->next) {
  382. iter->saved_crtc = drmModeGetCrtc(fd, iter->crtc);
  383. buf = &iter->bufs[iter->front_buf];
  384. ret = drmModeSetCrtc(fd, iter->crtc, buf->fb, 0, 0,
  385. &iter->conn, 1, &iter->mode);
  386. if (ret)
  387. fprintf(stderr, "cannot set CRTC for connector %u (%d): %m\n",
  388. iter->conn, errno);
  389. }
  390. /* draw some colors for 5seconds */
  391. modeset_draw(fd);
  392. /* cleanup everything */
  393. modeset_cleanup(fd);
  394. ret = 0;
  395. out_close:
  396. close(fd);
  397. out_return:
  398. if (ret) {
  399. errno = -ret;
  400. fprintf(stderr, "modeset failed with error %d: %m\n", errno);
  401. } else {
  402. fprintf(stderr, "exiting\n");
  403. }
  404. return ret;
  405. }
  406. /*
  407. * modeset_page_flip_event() is a callback-helper for modeset_draw() below.
  408. * Please see modeset_draw() for more information.
  409. *
  410. * Note that this does nothing if the device is currently cleaned up. This
  411. * allows to wait for outstanding page-flips during cleanup.
  412. */
  413. static void modeset_page_flip_event(int fd, unsigned int frame,
  414. unsigned int sec, unsigned int usec,
  415. void *data)
  416. {
  417. struct modeset_dev *dev = data;
  418. dev->pflip_pending = false;
  419. if (!dev->cleanup)
  420. modeset_draw_dev(fd, dev);
  421. }
  422. /*
  423. * modeset_draw() changes heavily from all previous examples. The rendering has
  424. * moved into another helper modeset_draw_dev() below, but modeset_draw() is now
  425. * responsible of controlling when we have to redraw the outputs.
  426. *
  427. * So what we do: first redraw all outputs. We initialize the r/g/b/_up
  428. * variables of each output first, although, you can safely ignore these.
  429. * They're solely used to compute the next color. Then we call
  430. * modeset_draw_dev() for each output. This function _always_ redraws the output
  431. * and schedules a buffer-swap/flip for the next vertical-blank.
  432. * We now have to wait for each vertical-blank to happen so we can draw the next
  433. * frame. If a vblank happens, we simply call modeset_draw_dev() again and wait
  434. * for the next vblank.
  435. *
  436. * Note: Different monitors can have different refresh-rates. That means, a
  437. * vblank event is always assigned to a CRTC. Hence, we get different vblank
  438. * events for each CRTC/modeset_dev that we use. This also means, that our
  439. * framerate-controlled color-morphing is different on each monitor. If you want
  440. * exactly the same frame on all monitors, we would have to share the
  441. * color-values between all devices. However, for simplicity reasons, we don't
  442. * do this here.
  443. *
  444. * So the last piece missing is how we get vblank events. libdrm provides
  445. * drmWaitVBlank(), however, we aren't interested in _all_ vblanks, but only in
  446. * the vblanks for our page-flips. We could use drmWaitVBlank() but there is a
  447. * more convenient way: drmModePageFlip()
  448. * drmModePageFlip() schedules a buffer-flip for the next vblank and then
  449. * notifies us about it. It takes a CRTC-id, fb-id and an arbitrary
  450. * data-pointer and then schedules the page-flip. This is fully asynchronous and
  451. * returns immediately.
  452. * When the page-flip happens, the DRM-fd will become readable and we can call
  453. * drmHandleEvent(). This will read all vblank/page-flip events and call our
  454. * modeset_page_flip_event() callback with the data-pointer that we passed to
  455. * drmModePageFlip(). We simply call modeset_draw_dev() then so the next frame
  456. * is rendered..
  457. *
  458. *
  459. * So modeset_draw() is reponsible of waiting for the page-flip/vblank events
  460. * for _all_ currently used output devices and schedule a redraw for them. We
  461. * could easily do this in a while (1) { drmHandleEvent() } loop, however, this
  462. * example shows how you can use the DRM-fd to integrate this into your own
  463. * main-loop. If you aren't familiar with select(), poll() or epoll, please read
  464. * it up somewhere else. There is plenty of documentation elsewhere on the
  465. * internet.
  466. *
  467. * So what we do is adding the DRM-fd and the keyboard-input-fd (more precisely:
  468. * the stdin FD) to a select-set and then we wait on this set. If the DRM-fd is
  469. * readable, we call drmHandleEvents() to handle the page-flip events. If the
  470. * input-fd is readable, we exit. So on any keyboard input we exit this loop
  471. * (you need to press RETURN after each keyboard input to make this work).
  472. */
  473. static void modeset_draw(int fd)
  474. {
  475. int ret;
  476. fd_set fds;
  477. time_t start, cur;
  478. struct timeval v;
  479. drmEventContext ev;
  480. struct modeset_dev *iter;
  481. /* init variables */
  482. srand(time(&start));
  483. FD_ZERO(&fds);
  484. memset(&v, 0, sizeof(v));
  485. memset(&ev, 0, sizeof(ev));
  486. /* Set this to only the latest version you support. Version 2
  487. * introduced the page_flip_handler, so we use that. */
  488. ev.version = 2;
  489. ev.page_flip_handler = modeset_page_flip_event;
  490. /* redraw all outputs */
  491. for (iter = modeset_list; iter; iter = iter->next) {
  492. iter->r = rand() % 0xff;
  493. iter->g = rand() % 0xff;
  494. iter->b = rand() % 0xff;
  495. iter->r_up = iter->g_up = iter->b_up = true;
  496. modeset_draw_dev(fd, iter);
  497. }
  498. /* wait 5s for VBLANK or input events */
  499. while (time(&cur) < start + 5) {
  500. FD_SET(0, &fds);
  501. FD_SET(fd, &fds);
  502. v.tv_sec = start + 5 - cur;
  503. ret = select(fd + 1, &fds, NULL, NULL, &v);
  504. if (ret < 0) {
  505. fprintf(stderr, "select() failed with %d: %m\n", errno);
  506. break;
  507. } else if (FD_ISSET(0, &fds)) {
  508. fprintf(stderr, "exit due to user-input\n");
  509. break;
  510. } else if (FD_ISSET(fd, &fds)) {
  511. drmHandleEvent(fd, &ev);
  512. }
  513. }
  514. }
  515. /*
  516. * A short helper function to compute a changing color value. No need to
  517. * understand it.
  518. */
  519. static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod)
  520. {
  521. uint8_t next;
  522. next = cur + (*up ? 1 : -1) * (rand() % mod);
  523. if ((*up && next < cur) || (!*up && next > cur)) {
  524. *up = !*up;
  525. next = cur;
  526. }
  527. return next;
  528. }
  529. /*
  530. * modeset_draw_dev() is a new function that redraws the screen of a single
  531. * output. It takes the DRM-fd and the output devices as arguments, redraws a
  532. * new frame and schedules the page-flip for the next vsync.
  533. *
  534. * This function does the same as modeset_draw() did in the previous examples
  535. * but only for a single output device now.
  536. * After we are done rendering a frame, we have to swap the buffers. Instead of
  537. * calling drmModeSetCrtc() as we did previously, we now want to schedule this
  538. * page-flip for the next vertical-blank (vblank). We use drmModePageFlip() for
  539. * this. It takes the CRTC-id and FB-id and will asynchronously swap the buffers
  540. * when the next vblank occurs. Note that this is done by the kernel, so neither
  541. * a thread is started nor any other magic is done in libdrm.
  542. * The DRM_MODE_PAGE_FLIP_EVENT flag tells drmModePageFlip() to send us a
  543. * page-flip event on the DRM-fd when the page-flip happened. The last argument
  544. * is a data-pointer that is returned with this event.
  545. * If we wouldn't pass this flag, we would not get notified when the page-flip
  546. * happened.
  547. *
  548. * Note: If you called drmModePageFlip() and directly call it again, it will
  549. * return EBUSY if the page-flip hasn't happened in between. So you almost
  550. * always want to pass DRM_MODE_PAGE_FLIP_EVENT to get notified when the
  551. * page-flip happens so you know when to render the next frame.
  552. * If you scheduled a page-flip but call drmModeSetCrtc() before the next
  553. * vblank, then the scheduled page-flip will become a no-op. However, you will
  554. * still get notified when it happens and you still cannot call
  555. * drmModePageFlip() again until it finished. So to sum it up: there is no way
  556. * to effectively cancel a page-flip.
  557. *
  558. * If you wonder why drmModePageFlip() takes fewer arguments than
  559. * drmModeSetCrtc(), then you should take into account, that drmModePageFlip()
  560. * reuses the arguments from drmModeSetCrtc(). So things like connector-ids,
  561. * x/y-offsets and so on have to be set via drmModeSetCrtc() first before you
  562. * can use drmModePageFlip()! We do this in main() as all the previous examples
  563. * did, too.
  564. */
  565. static void modeset_draw_dev(int fd, struct modeset_dev *dev)
  566. {
  567. struct modeset_buf *buf;
  568. unsigned int j, k, off;
  569. int ret;
  570. dev->r = next_color(&dev->r_up, dev->r, 20);
  571. dev->g = next_color(&dev->g_up, dev->g, 10);
  572. dev->b = next_color(&dev->b_up, dev->b, 5);
  573. buf = &dev->bufs[dev->front_buf ^ 1];
  574. for (j = 0; j < buf->height; ++j) {
  575. for (k = 0; k < buf->width; ++k) {
  576. off = buf->stride * j + k * 4;
  577. *(uint32_t*)&buf->map[off] =
  578. (dev->r << 16) | (dev->g << 8) | dev->b;
  579. }
  580. }
  581. ret = drmModePageFlip(fd, dev->crtc, buf->fb,
  582. DRM_MODE_PAGE_FLIP_EVENT, dev);
  583. if (ret) {
  584. fprintf(stderr, "cannot flip CRTC for connector %u (%d): %m\n",
  585. dev->conn, errno);
  586. } else {
  587. dev->front_buf ^= 1;
  588. dev->pflip_pending = true;
  589. }
  590. }
  591. /*
  592. * modeset_cleanup() stays mostly the same. However, before resetting a CRTC to
  593. * its previous state, we wait for any outstanding page-flip to complete. This
  594. * isn't strictly neccessary, however, some DRM drivers are known to be buggy if
  595. * we call drmModeSetCrtc() if there is a pending page-flip.
  596. * Furthermore, we don't want any pending page-flips when our application exist.
  597. * Because another application might pick up the DRM device and try to schedule
  598. * their own page-flips which might then fail as long as our page-flip is
  599. * pending.
  600. * So lets be safe here and simply wait for any page-flips to complete. This is
  601. * a blocking operation, but it's mostly just <16ms so we can ignore that.
  602. */
  603. static void modeset_cleanup(int fd)
  604. {
  605. struct modeset_dev *iter;
  606. drmEventContext ev;
  607. int ret;
  608. /* init variables */
  609. memset(&ev, 0, sizeof(ev));
  610. ev.version = DRM_EVENT_CONTEXT_VERSION;
  611. ev.page_flip_handler = modeset_page_flip_event;
  612. while (modeset_list) {
  613. /* remove from global list */
  614. iter = modeset_list;
  615. modeset_list = iter->next;
  616. /* if a pageflip is pending, wait for it to complete */
  617. iter->cleanup = true;
  618. fprintf(stderr, "wait for pending page-flip to complete...\n");
  619. while (iter->pflip_pending) {
  620. ret = drmHandleEvent(fd, &ev);
  621. if (ret)
  622. break;
  623. }
  624. /* restore saved CRTC configuration */
  625. if (!iter->pflip_pending)
  626. drmModeSetCrtc(fd,
  627. iter->saved_crtc->crtc_id,
  628. iter->saved_crtc->buffer_id,
  629. iter->saved_crtc->x,
  630. iter->saved_crtc->y,
  631. &iter->conn,
  632. 1,
  633. &iter->saved_crtc->mode);
  634. drmModeFreeCrtc(iter->saved_crtc);
  635. /* destroy framebuffers */
  636. modeset_destroy_fb(fd, &iter->bufs[1]);
  637. modeset_destroy_fb(fd, &iter->bufs[0]);
  638. /* free allocated memory */
  639. free(iter);
  640. }
  641. }
  642. /*
  643. * This example shows how to make the kernel handle page-flips and how to wait
  644. * for them in user-space. The select() example here should show you how you can
  645. * integrate these loops into your own applications without the need for a
  646. * separate modesetting thread.
  647. *
  648. * However, please note that vsync'ed double-buffering doesn't solve all
  649. * problems. Imagine that you cannot render a frame fast enough to satisfy all
  650. * vertical-blanks. In this situation, you don't want to wait after scheduling a
  651. * page-flip until the vblank happens to draw the next frame. A solution for
  652. * this is triple-buffering. It should be farily easy to extend this example to
  653. * use triple-buffering, but feel free to contact me if you have any questions
  654. * about it.
  655. * Also note that the DRM kernel API is quite limited if you want to reschedule
  656. * page-flips that haven't happened, yet. You cannot call drmModePageFlip()
  657. * twice in a single scanout-period. The behavior of drmModeSetCrtc() while a
  658. * page-flip is pending might also be unexpected.
  659. * Unfortunately, there is no ultimate solution to all modesetting problems.
  660. * This example shows the tools to do vsync'ed page-flips, however, it depends
  661. * on your use-case how you have to implement it.
  662. *
  663. * If you want more code, I can recommend reading the source-code of:
  664. * - plymouth (which uses dumb-buffers like this example; very easy to understand)
  665. * - kmscon (which uses libuterm to do this)
  666. * - wayland (very sophisticated DRM renderer; hard to understand fully as it
  667. * uses more complicated techniques like DRM planes)
  668. * - xserver (very hard to understand as it is split across many files/projects)
  669. *
  670. * Any feedback is welcome. Feel free to use this code freely for your own
  671. * documentation or projects.
  672. *
  673. * - Hosted on http://github.com/dvdhrm/docs
  674. * - Written by David Rheinsberg <david.rheinsberg@gmail.com>
  675. */