Advanced DRM Mode-Setting API

I recently wrote a short How-To that introduces the linux DRM Mode-Setting API. It didn’t use any advanced techniques but I got several responses that it is a great introduction if you want to get started with linux DRM Mode-Setting. So I decided to go further and extend the examples to use double-buffering and vsync’ed page-flips.

The first extension that I wrote can be found here:

https://github.com/dvdhrm/docs/blob/master/drm-howto/modeset-double-buffered.c

It extends the old example to use two buffers so we no longer render into the front-buffer. It reduces a lot of tearing that you get when using the single-buffered example. However, it is still not perfect as you might swap the front and back buffer during a scanout-period and the display-controller will use the new buffer in the middle of the screen. So I extended this further to do the page-flip during a vertical-blank period using drmModePageFlip(). You can find this example here:

https://github.com/dvdhrm/docs/blob/master/drm-howto/modeset-vsync.c

This example also shows how to wait for page-flip events and integrate it into any select(), poll() or epoll based event-loop. Everything regarding page-flip-timing beyond that point depends on the use-cases and can get very hard to get right. I recommend reading Owen Taylor’s posts #1 and #2 on frame-timing for compositors.

There are still many more things like “DRM hardware-accelerated rendering”, “DRM planes/overlays/sprites”, “DRM flink/dmabuf buffer-passing” that I want to write How-Tos for. But time is short around Christmas so that’ll have to wait until next year.

Feedback is always welcome and you can find my email address in all the examples. Happy reading!

23 thoughts on “Advanced DRM Mode-Setting API

  1. Mark Zhang

    Really excellent example for libdrm. Thanks David.
    I’m looking forward to your next articles of this series and also hope the manpage of libdrm which you proposed as well to be applied upstream early.🙂

    Reply
  2. Mark Zhang

    David, I found a bug in “modeset-vsync.c”. When we quit the “while” loop in “modeset_draw”(whatever user interrupts the loop or the timeout occurs), “modeset_cleanup” get executed and we call set crtc to restore the saved crtc. But we know there is a page flip likely is still running, so this page flip may redraw the screen after we cleaned up the screen. This makes the crtc restore no-ops.

    So I suggest add these codes before “modeset_cleanup” in main function:

    {
    drmVBlank vbl;

    /* Wait VBlank before cleanup. This avoids set crtc in the
    middle of a page flip. */
    vbl.request.type = DRM_VBLANK_RELATIVE;
    vbl.request.sequence = 1;
    ret = drmWaitVBlank(fd, &vbl);
    if (ret != 0) {
    fprintf(stderr, “drmWaitVBlank (relative) failed ret: %i\n”,
    ret);
    /* Anyway, goto cleanup. */
    }
    }

    Reply
    1. David Herrmann Post author

      As far as I know any drmModeSetCrtc() cancels pending page-flips or at least makes them a no-op. I talked to some intel-devs and this scenario is known to be buggy. However, I want this example to show the _expected_ behavior and not the real behavior (although I really should mention that many drivers currently implement this in the wrong way).

      Furthermore, I don’t like waiting for a VBLANK if this isn’t necessary. 16ms is quite noticeable in performance-critical paths. Therefore, I’d like to see a _DRM_VBLANK_IF_PENDING flag that performs the wait only if a page-flip is pending. Or, even better, get drmModeSetCrtc() cancel any scheduled page-flip unconditionally.

      Do you have any kernel code that shows that your race-condition really exists? (or is this even reproducable?) I will change this code then, but again as far as I know the drivers are supposed to work differently.
      But maybe it’s just better to always wait for a vblank if we have a pending page-flip. Just to be safe…

      Thanks for the great feedback! I hope I can complete the 3D-acceleration examples in the next weeks.

      Reply
      1. Mark Zhang

        Totally agree. Yes, the current version shows the _expected_ behavior and I agree with you that drmModeSetCrtc should cancel pageflip unconditionally while not to make user space program to worry about it.

        And yes, I can reproduce the issue on my laptop. It’s an ASUS laptop with an ATI graphic card inside. I use ATI’s open source “radeon” drm driver. When I run this program, it’s very likely(>50%) that finally I can’t get back to my origin screen until I added that VBlank waiting codes.

        I also skimmed the function “drm_mode_setcrtc” in kernel and I haven’t found it will cancel page flips. So I believe currently drmModeSetCrtc doesn’t have this behavior.

      2. David Herrmann Post author

        drm_mode_setcrtc just forwards the requests to the drivers. So the drivers would actually be responsible to cancel the page-flip. As I couldn’t reproduce the issue with i915 I never thought that it really existed. But seems like we need to add the WaitVBlank() call as workaround. I will update this part tomorrow, too. Thanks! But maybe I should also ping the driver maintainers in #dri-devel to fix this issue eventually.

  3. Mark Zhang

    Hi David,

    I’m lazy to find your mail in dri-devel maillist so I post here:

    I found another bug in the code. In function “modeset_find_crtc”, there is:

    /* we have found a CRTC, so save it and return */
    if (crtc >= 0) {
    ……

    Actually, the type of variable “crtc” is “unsigned int”, so this check always succeeds. In other words, even the crtc is set to “-1” in previous codes in this function(to indicate we can’t use this crtc), -1 will still be used as the final crtc id.

    So I suggest to change to:

    /* we have found a CRTC, so save it and return */
    if (crtc != -1) {
    ……

    Reply
    1. David Herrmann Post author

      It’s dh.herrmann-googlemail-com

      I fixed the issues you mentioned and pushed all to the repository. Some remarks:

      I used int32_t for the crtcs instead of != -1. I think that’s the cleaner solution. But I also think that 0 is an invalid ID so we could just use 0 instead of -1. I need to look that up again.

      The find_crtc() code now reuses the current encoder+crtc if possible as you suggested.

      During cleanup, I now wait for outstanding page-flips. I discussed this with Daniel Vetter and he said I shouldn’t do that at all as this is a driver bug and should be fixed in the kernel. Adding work-arounds just causes the bugs to be never fixed so I recommend you report that to the ATI devs.
      Anyway, I also avoid a drmWaitVBLank() call. The problem with this is, that it does not take a CRTC/connector ID so we can never be sure what VBlank it is waiting for. Furthermore, a page-flip might take multiple vblanks until it is performed if rendering isn’t done yet.
      i915 checks for all that in drmModeSetCrtc() so it’s safe.
      Anyway, what I did now is simply wait for PAGE_FLIP_COMPLETE events if a page-flip is pending. This is the nicer solution and guaranteed to work. Could you give it a try on your ATI card?

      Thanks for the feedback! Btw., the DRM manpages are now upstreamed, too.

      Reply
      1. Mark Zhang

        Thanks for the quick reply.
        1. Set invalid crtc id to 0 seems better. Because we get the id from drmModeRes’s “crtcs” array, and the element type in this array is “uint32_t”. So it’s better for us to keep the same type with it.

        2. Yes, I’ll write a mail to dri-devel to ask ATI guys to fix that, when I have a free slot.

        3. Agree. drmWaitVBlank is not a good solution. I will try your way – wait for PAGE_FLIP_COMPLETE event. Unfortunately, I sold my laptop just few days ago so I think I’ll try to find another “radeon” device for testing.🙂

        4. Congratulations. This tutorial is really helpful for guys who wanna participate in drm developing.

  4. Robert

    Hello,

    (sorry for writing in german)
    ich habe das Beispiel mit vsync kompiliert für mein Beaglebone black und grundsätzlich funktioniert es. Auf meinem CRT sehe ich aber ganz leicht am oberen Bildrand einen Streifen. Was kann das sein?

    Ich werde das mal mit einem digitalen Device (Projektor mit DLP) probieren, mal sehen obs am CRT liegt.

    Robert

    Reply
  5. Robert

    When I use the example with double buffering I do not see any glitches. I wil test it with an DLP beamer.

    Reply
    1. Robert

      Hmmm, I just changed the code (next_color) to

      static int color=1;

      static void modeset_draw_dev(int fd, struct modeset_dev *dev)
      {
      struct modeset_buf *buf;
      unsigned int j, k, off;
      int ret;

      dev->r = color ? 255:128;
      dev->g = color ? 128:255;
      dev->b = color ? 255:128;

      color=1-color;

      and now I do not see any glitches.

      Reply
      1. David Herrmann Post author

        You obviously see glitches if you don’t do double-buffering or vsync. It seems odd that they only appear on the top of the screen, but that might be inherent to CRTs.
        So yeah, you “fix” that by either using a static frame (you cannot get glitches there..) or using vsync+double-buffering

  6. Robert

    Hi,

    I have a next question. drmWaitVBlank returns that time when the vsync happend in the drmVBlank structure in seconds and microseconds. It seems that is the same as uptime. sysinfo reports only in seconds – no microseconds. How can I get the time base used by drmWaitVBlank in user space in microseconds?

    Thanks!

    Reply
  7. notaz

    What is the proper way to do double-buffering with planes? Should I be calling drmModeSetPlane() on every frame with different fb_id?

    drmModeSetPlane() seems to be blocking until flip and that isn’t nice.

    Reply
    1. David Herrmann Post author

      There is currently no proper way to do mode-setting with planes. An atomic-modesetting API is being worked on (since 2 years!) but we’re getting close. But I’d discourage you from using planes for anything but testing and cursors.

      Reply
      1. notaz

        I’m a bit surprised you say that, aren’t planes intended for doing video overlays, like for implementing xvideo under X? DRM isn’t so new, how are current drivers like i915 and radeon doing tear-free video, through driver specific extensions?

      2. David Herrmann Post author

        Yes, the API is crap. Planes expose really powerful hardware features and we’d all like to use them more extensively. However, as I said, there is no tear-free way to set planes. You cannot sync it to _anything_. Try for instance moving a browser-window were the flash-player uses a plane. You will notice the window and flash-content move at different times.

        weston tries to use drmWaitForVBlank() to do some fake-vsync, but that ioctl doesn’t take a pipe/crtc argument and doesn’t do the page-flip itself. It’s a neat work-around, but nothing you should advertise as feature to API users. Where does that leave us? Well, OpenGL.. The best way to do composing is through OpenGL render passes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s