1 /*
2  * Copyright © 2012 Linaro Limited
3  * Copyright © 2013 Canonical Ltd
4  *
5  * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
6  *
7  * glmark2 is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * glmark2.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Authors:
21  *  Simon Que
22  *  Jesse Barker
23  *  Alexandros Frantzis
24  */
25 #include "native-state-drm.h"
26 #include "log.h"
27 
28 #include <fcntl.h>
29 #include <libudev.h>
30 #include <cstring>
31 #include <string>
32 
33 /******************
34  * Public methods *
35  ******************/
36 
37 bool
init_display()38 NativeStateDRM::init_display()
39 {
40     if (!dev_)
41         init();
42 
43     return (dev_ != 0);
44 }
45 
46 void*
display()47 NativeStateDRM::display()
48 {
49     return static_cast<void*>(dev_);
50 }
51 
52 bool
create_window(WindowProperties const &)53 NativeStateDRM::create_window(WindowProperties const& /*properties*/)
54 {
55     if (!dev_) {
56         Log::error("Error: DRM device has not been initialized!\n");
57         return false;
58     }
59 
60     return true;
61 }
62 
63 void*
window(WindowProperties & properties)64 NativeStateDRM::window(WindowProperties& properties)
65 {
66     properties = WindowProperties(mode_->hdisplay,
67                                   mode_->vdisplay,
68                                   true, 0);
69     return static_cast<void*>(surface_);
70 }
71 
72 void
visible(bool)73 NativeStateDRM::visible(bool /*visible*/)
74 {
75 }
76 
77 bool
should_quit()78 NativeStateDRM::should_quit()
79 {
80     return should_quit_;
81 }
82 
83 void
flip()84 NativeStateDRM::flip()
85 {
86     if (!crtc_set_ && drmSetMaster(fd_) < 0) {
87         Log::error("Failed to become DRM master "
88                    "(hint: glmark2-drm needs to be run in a VT)\n");
89         should_quit_ = true;
90         return;
91     }
92 
93     gbm_bo* next = gbm_surface_lock_front_buffer(surface_);
94     fb_ = fb_get_from_bo(next);
95     unsigned int waiting(1);
96 
97     if (!next || !fb_) {
98         Log::error("Failed to get gbm front buffer\n");
99         return;
100     }
101 
102     if (!crtc_set_) {
103         int status = drmModeSetCrtc(fd_, encoder_->crtc_id, fb_->fb_id, 0, 0,
104                                     &connector_->connector_id, 1, mode_);
105         if (status >= 0) {
106             crtc_set_ = true;
107             bo_ = next;
108         }
109         else {
110             Log::error("Failed to set crtc: %d\n", status);
111         }
112         return;
113     }
114 
115     int status = drmModePageFlip(fd_, encoder_->crtc_id, fb_->fb_id,
116                                  DRM_MODE_PAGE_FLIP_EVENT, &waiting);
117     if (status < 0) {
118         Log::error("Failed to enqueue page flip: %d\n", status);
119         return;
120     }
121 
122     fd_set fds;
123     FD_ZERO(&fds);
124     FD_SET(fd_, &fds);
125     drmEventContext evCtx;
126     memset(&evCtx, 0, sizeof(evCtx));
127     evCtx.version = 2;
128     evCtx.page_flip_handler = page_flip_handler;
129 
130     while (waiting) {
131         status = select(fd_ + 1, &fds, 0, 0, 0);
132         if (status < 0) {
133             // Most of the time, select() will return an error because the
134             // user pressed Ctrl-C.  So, only print out a message in debug
135             // mode, and just check for the likely condition and release
136             // the current buffer object before getting out.
137             Log::debug("Error in select\n");
138             if (should_quit()) {
139                 gbm_surface_release_buffer(surface_, bo_);
140                 bo_ = next;
141             }
142             return;
143         }
144         drmHandleEvent(fd_, &evCtx);
145     }
146 
147     gbm_surface_release_buffer(surface_, bo_);
148     bo_ = next;
149 }
150 
151 /*******************
152  * Private methods *
153  *******************/
154 
155 /* Simple helpers */
156 
valid_fd(int fd)157 inline static bool valid_fd(int fd)
158 {
159     return fd >= 0;
160 }
161 
valid_drm_node_path(std::string const & provided_node_path)162 inline static bool valid_drm_node_path(std::string const& provided_node_path)
163 {
164     return !provided_node_path.empty();
165 }
166 
invalid_drm_node_path(std::string const & provided_node_path)167 inline static bool invalid_drm_node_path(std::string const& provided_node_path)
168 {
169     return !(valid_drm_node_path(provided_node_path));
170 }
171 
172 /* Udev methods */
173 // Udev detection functions
174 #define UDEV_TEST_FUNC_SIGNATURE(udev_identifier, device_identifier, syspath_identifier) \
175     struct udev * __restrict const udev_identifier, \
176     struct udev_device * __restrict const device_identifier, \
177     char const * __restrict syspath_identifier
178 
179 /* Omitting the parameter names is kind of ugly but is the only way
180  * to force G++ to forget about the unused parameters.
181  * Having big warnings during the compilation isn't very nice.
182  *
183  * These functions will be used as function pointers and should have
184  * the same signature to avoid weird stack related issues.
185  *
186  * Another way to deal with that issue will be to mark unused parameters
187  * with __attribute__((unused))
188  */
189 static bool udev_drm_test_virtual(
190     UDEV_TEST_FUNC_SIGNATURE(,,tested_node_syspath))
191 {
192     return strstr(tested_node_syspath, "virtual") != NULL;
193 }
194 
udev_drm_test_not_virtual(UDEV_TEST_FUNC_SIGNATURE (udev,current_device,tested_node_syspath))195 static bool udev_drm_test_not_virtual(
196     UDEV_TEST_FUNC_SIGNATURE(udev, current_device, tested_node_syspath))
197 {
198     return !udev_drm_test_virtual(udev,
199                                   current_device,
200                                   tested_node_syspath);
201 }
202 
203 static bool
204 udev_drm_test_primary_gpu(UDEV_TEST_FUNC_SIGNATURE(, current_device,))
205 {
206     bool is_main_gpu = false;
207 
208     auto const drm_node_parent = udev_device_get_parent(current_device);
209 
210     /* While tempting, using udev_device_unref will generate issues
211      * when unreferencing the child in udev_get_node_that_pass_in_enum
212      *
213      * udev_device_unref WILL unreference the parent, so avoid doing
214      * that here.
215      *
216      * ( See udev sources : src/libudev/libudev-device.c )
217      */
218     if (drm_node_parent != NULL) {
219         is_main_gpu =
220             (udev_device_get_sysattr_value(drm_node_parent, "boot_vga")
221              != NULL);
222     }
223 
224     return is_main_gpu;
225 }
226 
227 /* Test if the drm-device is actually modeset capable.
228  * Render-only devices cannot drive an actual display,
229  * so the GETRESOURCES ioctl will fail in that case.
230  */
udev_drm_test_modeset(std::string const & dev_path)231 static bool udev_drm_test_modeset(std::string const& dev_path)
232 {
233     struct drm_mode_card_res res {};
234     int fd, ret;
235 
236     fd = open(dev_path.c_str(), O_RDWR);
237     if (!valid_fd(fd))
238         return false;
239 
240     ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res);
241     drmClose(fd);
242 
243     return !ret;
244 }
245 
246 static std::string
247 udev_get_node_that_pass_in_enum(
248     struct udev * __restrict const udev,
249     struct udev_enumerate * __restrict const dev_enum,
250     bool (* check_function)(UDEV_TEST_FUNC_SIGNATURE(,,)))
251 {
252     std::string result;
253 
254     auto current_element = udev_enumerate_get_list_entry(dev_enum);
255 
256     while (current_element && result.empty()) {
257         char const * __restrict current_element_sys_path =
258             udev_list_entry_get_name(current_element);
259 
260         if (current_element_sys_path) {
261             struct udev_device * current_device =
262                 udev_device_new_from_syspath(udev,
263                                              current_element_sys_path);
264             auto check_passed = check_function(
265                 udev, current_device, current_element_sys_path);
266 
267             if (check_passed) {
268                 const char * device_node_path =
269                     udev_device_get_devnode(current_device);
270 
271                 if (device_node_path &&
272                     udev_drm_test_modeset(device_node_path)) {
273                         result = device_node_path;
274                 }
275 
276             }
277 
278             udev_device_unref(current_device);
279         }
280 
281         current_element = udev_list_entry_get_next(current_element);
282     }
283 
284     return result;
285 }
286 
287 /* Inspired by KWin detection mechanism */
288 /* And yet KWin got it wrong too, it seems.
289  * 1 - Looking for the primary GPU by checking the flag 'boot_vga'
290  *     won't get you far with some embedded chipsets, like Rockchip.
291  * 2 - Looking for a GPU plugged in PCI will fail on various embedded
292  *     devices !
293  * 3 - Looking for a render node is not guaranteed to work on some
294  *     poorly maintained DRM drivers, which plague some embedded
295  *     devices too...
296  *
297  * So, we won't play too smart here.
298  * - We first check for a primary GPU plugged in PCI with the 'boot_vga'
299  *   attribute, to take care of Desktop users using multiple GPU.
300  * - Then, we just check for a DRM node that is not virtual
301  * - At least, we use the first virtual node we get, if we didn't find
302  *   anything yet.
303  * This should take care of almost every potential use case.
304  *
305  * The remaining ones will be dealt with an additional option to
306  * specify the DRM dev node manually.
307  */
udev_main_gpu_drm_node_path()308 static std::string udev_main_gpu_drm_node_path()
309 {
310     Log::debug("Using Udev to detect the right DRM node to use\n");
311     auto udev = udev_new();
312     auto dev_enumeration = udev_enumerate_new(udev);
313 
314     udev_enumerate_add_match_subsystem(dev_enumeration, "drm");
315     udev_enumerate_add_match_sysname(dev_enumeration, "card[0-9]*");
316     udev_enumerate_scan_devices(dev_enumeration);
317 
318     Log::debug("Looking for the main GPU DRM node...\n");
319     std::string node_path = udev_get_node_that_pass_in_enum(
320         udev, dev_enumeration, udev_drm_test_primary_gpu);
321 
322     if (invalid_drm_node_path(node_path)) {
323         Log::debug("Not found!\n");
324         Log::debug("Looking for a concrete GPU DRM node...\n");
325         node_path = udev_get_node_that_pass_in_enum(
326             udev, dev_enumeration, udev_drm_test_not_virtual);
327     }
328     if (invalid_drm_node_path(node_path)) {
329         Log::debug("Not found!?\n");
330         Log::debug("Looking for a virtual GPU DRM node...\n");
331         node_path = udev_get_node_that_pass_in_enum(
332             udev, dev_enumeration, udev_drm_test_virtual);
333     }
334     if (invalid_drm_node_path(node_path)) {
335         Log::debug("Not found.\n");
336         Log::debug("Cannot find a single DRM node using UDEV...\n");
337     }
338 
339     if (valid_drm_node_path(node_path)) {
340         Log::debug("Success!\n");
341     }
342 
343     udev_enumerate_unref(dev_enumeration);
344     udev_unref(udev);
345 
346     return node_path;
347 }
348 
open_using_udev_scan()349 static int open_using_udev_scan()
350 {
351     auto dev_path = udev_main_gpu_drm_node_path();
352 
353     int fd = -1;
354     if (valid_drm_node_path(dev_path)) {
355         Log::debug("Trying to use the DRM node %s\n", dev_path.c_str());
356         fd = open(dev_path.c_str(), O_RDWR);
357     }
358     else {
359         Log::error("Can't determine the main graphic card "
360                    "DRM device node\n");
361     }
362 
363     if (!valid_fd(fd)) {
364         // %m is GLIBC specific... Maybe use strerror here...
365         Log::error("Tried to use '%s' but failed.\nReason : %m\n",
366                    dev_path.c_str());
367     }
368     else
369         Log::debug("Success!\n");
370 
371     return fd;
372 }
373 
374 /* -- End of Udev helpers -- */
375 
376 /*
377  * This method is there to take care of cases that would not be handled
378  * by open_using_udev_scan. This should not happen.
379  *
380  * If your driver defines a /dev/dri/cardX node and open_using_udev_scan
381  * were not able to detect it, you should probably file an issue.
382  *
383  */
open_using_module_checking()384 static int open_using_module_checking()
385 {
386     static const char* drm_modules[] = {
387         "i915",
388         "imx-drm",
389         "nouveau",
390         "radeon",
391         "vmgfx",
392         "omapdrm",
393         "exynos",
394         "pl111",
395         "vc4",
396         "msm",
397         "meson",
398         "rockchip",
399         "sun4i-drm",
400         "stm",
401     };
402 
403     int fd = -1;
404     unsigned int num_modules(sizeof(drm_modules)/sizeof(drm_modules[0]));
405     for (unsigned int m = 0; m < num_modules; m++) {
406         fd = drmOpen(drm_modules[m], 0);
407         if (fd < 0) {
408             Log::debug("Failed to open DRM module '%s'\n", drm_modules[m]);
409             continue;
410         }
411         Log::debug("Opened DRM module '%s'\n", drm_modules[m]);
412         break;
413     }
414 
415     return fd;
416 }
417 
418 void
fb_destroy_callback(gbm_bo * bo,void * data)419 NativeStateDRM::fb_destroy_callback(gbm_bo* bo, void* data)
420 {
421     DRMFBState* fb = reinterpret_cast<DRMFBState*>(data);
422     if (fb && fb->fb_id) {
423         drmModeRmFB(fb->fd, fb->fb_id);
424     }
425     delete fb;
426     gbm_device* dev = gbm_bo_get_device(bo);
427     Log::debug("Got GBM device handle %p from buffer object\n", dev);
428 }
429 
430 NativeStateDRM::DRMFBState*
fb_get_from_bo(gbm_bo * bo)431 NativeStateDRM::fb_get_from_bo(gbm_bo* bo)
432 {
433     if (!bo) {
434         return NULL;
435     }
436 
437     DRMFBState* fb = reinterpret_cast<DRMFBState*>(gbm_bo_get_user_data(bo));
438     if (fb) {
439         return fb;
440     }
441 
442     unsigned int width = gbm_bo_get_width(bo);
443     unsigned int height = gbm_bo_get_height(bo);
444     unsigned int stride = gbm_bo_get_stride(bo);
445     unsigned int handle = gbm_bo_get_handle(bo).u32;
446     unsigned int fb_id(0);
447     int status = drmModeAddFB(fd_, width, height, 24, 32, stride, handle, &fb_id);
448     if (status < 0) {
449         Log::error("Failed to create FB: %d\n", status);
450         return 0;
451     }
452 
453     fb = new DRMFBState();
454     fb->fd = fd_;
455     fb->bo = bo;
456     fb->fb_id = fb_id;
457 
458     gbm_bo_set_user_data(bo, fb, fb_destroy_callback);
459     return fb;
460 }
461 
462 bool
init_gbm()463 NativeStateDRM::init_gbm()
464 {
465     dev_ = gbm_create_device(fd_);
466     if (!dev_) {
467         Log::error("Failed to create GBM device\n");
468         return false;
469     }
470 
471     surface_ = gbm_surface_create(dev_, mode_->hdisplay, mode_->vdisplay,
472                                   GBM_FORMAT_XRGB8888,
473                                   GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
474     if (!surface_) {
475         Log::error("Failed to create GBM surface\n");
476         return false;
477     }
478 
479     return true;
480 }
481 
482 bool
init()483 NativeStateDRM::init()
484 {
485     // TODO: The user should be able to define *exactly* which device
486     //       node to open and the program should try to open only
487     //       this node, in order to take care of unknown use cases.
488     int fd = open_using_udev_scan();
489 
490     if (!valid_fd(fd)) {
491         fd = open_using_module_checking();
492     }
493 
494     if (!valid_fd(fd)) {
495         Log::error("Failed to find a suitable DRM device\n");
496         return false;
497     }
498 
499     fd_ = fd;
500 
501     resources_ = drmModeGetResources(fd);
502     if (!resources_) {
503         Log::error("drmModeGetResources failed\n");
504         return false;
505     }
506 
507     // Find a connected connector
508     for (int c = 0; c < resources_->count_connectors; c++) {
509         connector_ = drmModeGetConnector(fd, resources_->connectors[c]);
510         if (DRM_MODE_CONNECTED == connector_->connection) {
511             break;
512         }
513         drmModeFreeConnector(connector_);
514         connector_ = 0;
515     }
516 
517     if (!connector_) {
518         Log::error("Failed to find a suitable connector\n");
519         return false;
520     }
521 
522     // Find the best resolution (we will always operate full-screen).
523     unsigned int bestArea(0);
524     for (int m = 0; m < connector_->count_modes; m++) {
525         drmModeModeInfo* curMode = &connector_->modes[m];
526         unsigned int curArea = curMode->hdisplay * curMode->vdisplay;
527         if (curArea > bestArea) {
528             mode_ = curMode;
529             bestArea = curArea;
530         }
531     }
532 
533     if (!mode_) {
534         Log::error("Failed to find a suitable mode\n");
535         return false;
536     }
537 
538     // Find a suitable encoder
539     for (int e = 0; e < resources_->count_encoders; e++) {
540         bool found = false;
541         encoder_ = drmModeGetEncoder(fd_, resources_->encoders[e]);
542         for (int ce = 0; ce < connector_->count_encoders; ce++) {
543             if (encoder_ && encoder_->encoder_id == connector_->encoders[ce]) {
544                 found = true;
545                 break;
546             }
547         }
548         if (found)
549             break;
550         drmModeFreeEncoder(encoder_);
551         encoder_ = 0;
552     }
553 
554     // If encoder is not connected to the connector try to find
555     // a suitable one
556     if (!encoder_) {
557         for (int e = 0; e < connector_->count_encoders; e++) {
558             encoder_ = drmModeGetEncoder(fd, connector_->encoders[e]);
559             for (int c = 0; c < resources_->count_crtcs; c++) {
560                 if (encoder_->possible_crtcs & (1 << c)) {
561                     encoder_->crtc_id = resources_->crtcs[c];
562                     break;
563                 }
564             }
565             if (encoder_->crtc_id) {
566                 break;
567             }
568 
569             drmModeFreeEncoder(encoder_);
570             encoder_ = 0;
571         }
572     }
573 
574     if (!encoder_) {
575         Log::error("Failed to find a suitable encoder\n");
576         return false;
577     }
578 
579     if (!init_gbm()) {
580         return false;
581     }
582 
583     crtc_ = drmModeGetCrtc(fd_, encoder_->crtc_id);
584     if (!crtc_) {
585         // if there is no current CRTC, make sure to attach a suitable one
586         for (int c = 0; c < resources_->count_crtcs; c++) {
587             if (encoder_->possible_crtcs & (1 << c)) {
588                 encoder_->crtc_id = resources_->crtcs[c];
589                 break;
590             }
591         }
592     }
593 
594     signal(SIGINT, &NativeStateDRM::quit_handler);
595 
596     return true;
597 }
598 
599 volatile std::sig_atomic_t NativeStateDRM::should_quit_(false);
600 
601 void
quit_handler(int)602 NativeStateDRM::quit_handler(int /*signo*/)
603 {
604     should_quit_ = true;
605 }
606 
607 void
page_flip_handler(int,unsigned int,unsigned int,unsigned int,void * data)608 NativeStateDRM::page_flip_handler(int/*  fd */, unsigned int /* frame */, unsigned int /* sec */, unsigned int /* usec */, void* data)
609 {
610     unsigned int* waiting = reinterpret_cast<unsigned int*>(data);
611     *waiting = 0;
612 }
613 
614 void
cleanup()615 NativeStateDRM::cleanup()
616 {
617     // Restore CRTC state if necessary
618     if (crtc_) {
619         int status = drmModeSetCrtc(fd_, crtc_->crtc_id, crtc_->buffer_id,
620                                     crtc_->x, crtc_->y, &connector_->connector_id,
621                                     1, &crtc_->mode);
622         if (status < 0) {
623             Log::error("Failed to restore original CRTC: %d\n", status);
624         }
625         drmModeFreeCrtc(crtc_);
626         crtc_ = 0;
627     }
628     if (surface_) {
629         gbm_surface_destroy(surface_);
630         surface_ = 0;
631     }
632     if (dev_) {
633         gbm_device_destroy(dev_);
634         dev_ = 0;
635     }
636     if (connector_) {
637         drmModeFreeConnector(connector_);
638         connector_ = 0;
639     }
640     if (encoder_) {
641         drmModeFreeEncoder(encoder_);
642         encoder_ = 0;
643     }
644     if (resources_) {
645         drmModeFreeResources(resources_);
646         resources_ = 0;
647     }
648     if (fd_ > 0) {
649         drmClose(fd_);
650     }
651     fd_ = 0;
652     mode_ = 0;
653 }
654