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