DrmGLESGraphicSystem: Improves the repaint efficiency in DRM backend.
[profile/ivi/layer-management.git] / LayerManagerPlugins / Renderers / Graphic / src / GraphicSystems / DrmGLESGraphicSystem.cpp
1 /***************************************************************************
2  *
3  * Copyright 2010,2011 BMW Car IT GmbH
4  * Copyright (C) 2011 DENSO CORPORATION and Robert Bosch Car Multimedia Gmbh
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *        http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ****************************************************************************/
20
21 #include "GraphicSystems/DrmGLESGraphicSystem.h"
22 #include "IlmMatrix.h"
23 #include "string.h"
24 #include "EGL/egl.h"
25 #include "EGL/eglext.h"
26 #include "GLES2/gl2.h"
27 #include "Bitmap.h"
28 #include "PlatformSurfaces/WaylandPlatformSurface.h"
29 #include "WindowSystems/WaylandBaseWindowSystem.h"
30
31 #include "wayland-server.h"
32
33 #include <errno.h>
34 #include <drm_fourcc.h>
35 #include <drm_mode.h>
36 #include <gbm.h>
37 #include <libudev.h>
38
39 static drmModeModeInfo builtin_800x480_for_Crossville = {
40         31746,
41         800, 859, 999, 999, 0,
42         480, 507, 538, 524, 0,
43         0,
44         DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
45         0,
46         "800x480"
47 };
48
49 static void
50 pageFlipHandler(int /*fd*/, unsigned int /*frame*/, unsigned int /*sec*/,
51                                 unsigned int /*usec*/, void *data)
52 {
53         struct DrmOutput *output = static_cast<struct DrmOutput*>(data);
54
55         output->pageFlipPending = 0;
56
57         if (output->current)
58         {
59                 gbm_surface_release_buffer(output->surface, output->current->bo);
60         }
61
62         output->current = output->next;
63         output->next = NULL;
64
65         if (output->windowSystem)
66         {
67                 output->windowSystem->finishFrame();
68         }
69 }
70
71 static int
72 onDrmInput(int fd, uint32_t /*mask*/, void * /*data*/)
73 {
74         drmEventContext evctx;
75         memset(&evctx, 0, sizeof evctx);
76
77         evctx.version = DRM_EVENT_CONTEXT_VERSION;
78         evctx.page_flip_handler = pageFlipHandler;
79         evctx.vblank_handler = NULL;
80
81         drmHandleEvent(fd, &evctx);
82
83         return 1;
84 }
85
86 static void
87 drmFbDestroyCallback(struct gbm_bo *bo, void *data)
88 {
89         struct DrmFb *fb = (struct DrmFb*)data;
90         struct gbm_device *gbm = gbm_bo_get_device(bo);
91
92         if (fb->fbId)
93                 drmModeRmFB(gbm_device_get_fd(gbm), fb->fbId);
94
95         free(data);
96 }
97
98 struct DrmFb*
99 drmFbGetFromBo(struct gbm_bo *bo, int fdDev, struct DrmOutput *output)
100 {
101         struct DrmFb *fb = (struct DrmFb*)gbm_bo_get_user_data(bo);
102         uint32_t width, height, stride, handle;
103         int ret;
104
105         if (fb)
106                 return fb;
107
108         fb = (struct DrmFb*)malloc(sizeof *fb);
109         fb->bo = bo;
110         fb->output = output;
111
112         width  = gbm_bo_get_width(bo);
113         height = gbm_bo_get_height(bo);
114         stride = gbm_bo_get_stride(bo);
115         handle = gbm_bo_get_handle(bo).u32;
116
117         ret = drmModeAddFB(fdDev, width, height, 24, 32, stride, handle, &fb->fbId);
118         if (ret)
119         {
120                 return NULL;
121         }
122
123         gbm_bo_set_user_data(bo, fb, drmFbDestroyCallback);
124
125         return fb;
126 }
127
128 DrmGLESGraphicSystem::DrmGLESGraphicSystem(int windowWidth, int windowHeight,
129                                      PfnShaderProgramCreator shaderProgram)
130 : GLESGraphicsystem(windowWidth, windowHeight, shaderProgram)
131 , m_gbm(NULL)
132 , m_fdDev(-1)
133 , m_crtcs(NULL)
134 , m_crtcsNum(0)
135 , m_crtcAllocator(0)
136 , m_connectorAllocator(0)
137 , m_currentOutput(NULL)
138 {
139     LOG_DEBUG("DrmGLESGraphicSystem", "creating DrmGLESGraphicSystem");
140
141         m_pfEglBindWaylandDisplayWL = (PFNEGLBINDWAYLANDDISPLAYWL)eglGetProcAddress("eglBindWaylandDisplayWL");
142         m_pfEglUnbindWaylandDisplayWL = (PFNEGLUNBINDWAYLANDDISPLAYWL)eglGetProcAddress("eglUnbindWaylandDisplayWL");
143
144         if (!m_pfEglBindWaylandDisplayWL ||
145                 !m_pfEglUnbindWaylandDisplayWL)
146         {
147                 LOG_ERROR("DrmGLESGraphicSystem", "Query EGL extensions failed.");
148         }
149 }
150
151 DrmGLESGraphicSystem::~DrmGLESGraphicSystem()
152 {
153         WaylandBaseWindowSystem* windowSystem = dynamic_cast<WaylandBaseWindowSystem*>(m_baseWindowSystem);
154         struct wl_display* wlDisplay = windowSystem->getNativeDisplayHandle();
155
156         if (NULL != m_eglContext)
157         {
158                 eglDestroyContext(m_eglDisplay, m_eglContext);
159                 m_eglContext = NULL;
160         }
161
162         struct DrmOutput* output = NULL;
163         wl_list_for_each(output, &m_outputList, link) {
164                 if (NULL != output->eglSurface)
165                 {
166                         eglDestroySurface(m_eglDisplay, output->eglSurface);
167                         output->eglSurface = NULL;
168                 }
169
170                 if (NULL != output->surface)
171                 {
172                         gbm_surface_destroy(output->surface);
173                         output->surface = NULL;
174                 }
175                 free(output);
176         }
177
178         if (wlDisplay)
179                 m_pfEglUnbindWaylandDisplayWL(m_eglDisplay, wlDisplay);
180 }
181
182 bool DrmGLESGraphicSystem::init(EGLNativeDisplayType display, EGLNativeWindowType NativeWindow)
183 {
184         bool ret = true;
185     LOG_DEBUG("DrmGLESGraphicSystem", "init..display:"  << display <<
186                                                                           ", NativeWindow:" << NativeWindow);
187
188         m_nativeDisplay = display;
189         m_nativeWindow  = NativeWindow;
190
191         wl_list_init(&m_outputList);
192
193         m_gbm = (gbm_device*)display;
194         if (m_gbm == NULL)
195         {
196                 LOG_ERROR("DrmGLESGraphicSystem", "gbm device is NULL.");
197                 return false;
198         }
199
200         m_fdDev = gbm_device_get_fd(m_gbm);
201         if (m_fdDev < 0)
202         {
203                 LOG_ERROR("DrmGLESGraphicSystem", "failed to get device fd.");
204                 return false;
205         }
206
207         m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_gbm);
208         if (m_eglDisplay == EGL_NO_DISPLAY)
209         {
210                 LOG_ERROR("DrmGLESGraphicSystem", "failed to get EGL display.");
211                 return false;
212         }
213
214         EGLint major, minor;
215         if (!eglInitialize(m_eglDisplay, &major, &minor))
216         {
217                 LOG_ERROR("DrmGLESGraphicSystem", "failed to initialising EGL.");
218                 return false;
219         }
220
221         static const EGLint configAttribs[] = {
222                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
223                 EGL_RED_SIZE,   1,
224                 EGL_GREEN_SIZE, 1,
225                 EGL_BLUE_SIZE,  1,
226                 EGL_ALPHA_SIZE, 0,
227                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
228                 EGL_NONE
229         };
230         EGLint n = 0;
231         if (!eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &n) || n != 1)
232         {
233                 LOG_ERROR("DrmGLESGraphicSystem", "failed to choose config.");
234                 return false;
235         }
236
237         if (!initializeSystem())
238         {
239                 LOG_ERROR("DrmGLESGraphicSystem", "failed to initialize system.");
240                 return false;
241         }
242
243         if (!GLESGraphicsystem::initOpenGLES(m_windowWidth, m_windowHeight))
244         {
245                 LOG_ERROR("DrmGLESGraphicSystem", "failed to initialize gles.");
246                 return false;
247         }
248
249     return ret;
250 }
251
252 void DrmGLESGraphicSystem::activateGraphicContext()
253 {
254 }
255
256 void DrmGLESGraphicSystem::updateScreenList(LmScreenList& screenList)
257 {
258     LmScreenListIterator iter = screenList.begin();
259     LmScreenListIterator iterEnd = screenList.end();
260     for (; iter != iterEnd; ++iter)
261     {
262         delete (*iter);
263     }
264     screenList.clear();
265
266     struct DrmOutput* output = NULL;
267     wl_list_for_each(output, &m_outputList, link)
268     {
269         LmScreen* lmScreen = new LmScreen(output->screenID, "");
270         screenList.push_back(lmScreen);
271     }
272 }
273
274 void DrmGLESGraphicSystem::switchScreen(uint screenID)
275 {
276     // Actually, when renderSWLayers is called, rendering target buffer is switched
277     // because of avoiding overhead of switching display.
278     struct DrmOutput* output = NULL;
279     wl_list_for_each(output, &m_outputList, link)
280     {
281         if (output->screenID != screenID)
282         {
283             continue;
284         }
285
286         LOG_DEBUG("DrmGLESGraphicSystem", "switch screen:" << m_currentOutput->screenID);
287         m_currentOutput = output;
288         drmOutputPrepareRender(m_currentOutput);
289         break;
290     }
291 }
292
293 bool DrmGLESGraphicSystem::initializeSystem()
294 {
295     LOG_DEBUG("DrmGLESGraphicSystem", "initializeSystem IN");
296
297         WaylandBaseWindowSystem* windowSystem = dynamic_cast<WaylandBaseWindowSystem*>(m_baseWindowSystem);
298         struct wl_display* wlDisplay = windowSystem->getNativeDisplayHandle();
299
300         // bind display
301         m_pfEglBindWaylandDisplayWL(m_eglDisplay, wlDisplay);
302
303         if (!createOutputs())
304                 return false;
305
306         if (!eglBindAPI(EGL_OPENGL_ES_API))
307         {
308                 LOG_ERROR("DrmGLESGraphicSystem", "failed to bind api EGL_OPENGL_ES_API.");
309                 return false;
310         }
311
312         EGLint contextAttrs[] = {
313                 EGL_CONTEXT_CLIENT_VERSION, 2,
314                 EGL_NONE
315         };
316
317         m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttrs);
318         if (!m_eglContext)
319         {
320                 LOG_ERROR("DrmGLESGraphicSystem", "failed to create EGL context.");
321                 return false;
322         }
323
324         struct DrmOutput *output = wl_container_of(m_outputList.prev, output, link);
325
326         if (!eglMakeCurrent(m_eglDisplay, output->eglSurface, output->eglSurface, m_eglContext))
327         {
328                 LOG_ERROR("DrmGLESGraphicSystem", "failed to make context current.");
329                 return false;
330         }
331
332         struct wl_event_loop *loop = wl_display_get_event_loop(wlDisplay);
333         wl_event_loop_add_fd(loop, m_fdDev, WL_EVENT_READABLE, onDrmInput, NULL);
334
335     LOG_DEBUG("DrmGLESGraphicSystem", "initializeSystem OUT");
336
337         return true;
338 }
339
340 bool DrmGLESGraphicSystem::createOutputs()
341 {
342     LOG_DEBUG("DrmGLESGraphicSystem", "createOutputs IN");
343
344         drmModeConnector* connector;
345         drmModeRes*       resources;
346         int x = 0, y = 0;
347
348         resources = drmModeGetResources(m_fdDev);
349         if (!resources)
350         {
351                 LOG_DEBUG("DrmGLESGraphicSystem", "drmModeGetResources failed.");
352                 return false;
353         }
354
355         m_crtcs = (uint32_t*)calloc(resources->count_crtcs, sizeof(uint32_t));
356         if (!m_crtcs)
357         {
358                 drmModeFreeResources(resources);
359                 return false;
360         }
361         m_crtcsNum = resources->count_crtcs;
362
363         memcpy(m_crtcs, resources->crtcs, sizeof(uint32_t) * m_crtcsNum);
364
365         for (int ii = 0; ii < resources->count_connectors; ++ii)
366         {
367                 connector = drmModeGetConnector(m_fdDev, resources->connectors[ii]);
368                 if (connector == NULL)
369                         continue;
370
371                 if (connector->connection == DRM_MODE_CONNECTED)
372                 {
373                         if (createOutputForConnector(resources, connector, x, y) < 0)
374                         {
375                                 drmModeFreeConnector(connector);
376                                 continue;
377                         }
378
379                         //x += container_of(m_outputList.prev, struct DrmOutput, link)->currentMode->width;
380                 }
381
382                 drmModeFreeConnector(connector);
383         }
384
385         if (wl_list_empty(&m_outputList))
386         {
387                 LOG_ERROR("DrmGLESGraphicSystem", "DrmOutput list is empty.");
388                 drmModeFreeResources(resources);
389                 return false;
390         }
391
392         drmModeFreeResources(resources);
393
394     LOG_DEBUG("DrmGLESGraphicSystem", "createOutputs OUT");
395         return true;
396 }
397
398 int DrmGLESGraphicSystem::createOutputForConnector(drmModeRes* resources,
399                                                                                                    drmModeConnector* connector,
400                                                                                                    int, int)
401 {
402         LOG_DEBUG("DrmGLESGraphicSystem", "createOutputForConnector IN");
403         int ii;
404         drmModeEncoder* encoder;
405         struct DrmMode* drmMode = NULL;
406
407         encoder = drmModeGetEncoder(m_fdDev, connector->encoders[0]);
408         if (encoder == NULL){
409                 LOG_ERROR("DrmGLESGraphicSystem", "No encoder for connector.");
410                 return -1;
411         }
412
413         for (ii = 0; ii < resources->count_crtcs; ++ii)
414         {
415                 if (encoder->possible_crtcs & (1 << ii) &&
416                         !(m_crtcAllocator & (1 << resources->crtcs[ii])))
417                 {
418                         break;
419                 }
420         }
421         if (ii == resources->count_crtcs)
422         {
423                 LOG_ERROR("DrmGLESGraphicSystem", "No usable crtc for encoder.");
424                 return -1;
425         }
426
427         DrmOutput* output = (DrmOutput*)malloc(sizeof *output);
428         if (output == NULL)
429         {
430                 drmModeFreeEncoder(encoder);
431                 return -1;
432         }
433         memset(output, 0x00, sizeof *output);
434
435         output->windowSystem = m_baseWindowSystem;
436         output->fdDev       = m_fdDev;
437         output->crtcID      = resources->crtcs[ii];
438         output->connectorID = connector->connector_id;
439
440         m_crtcAllocator      |= (1 << output->crtcID);
441         m_connectorAllocator |= (1 << output->connectorID);
442
443         wl_list_init(&output->modeList);
444
445         output->orgCrtc = drmModeGetCrtc(m_fdDev, output->crtcID);
446         drmModeFreeEncoder(encoder);
447
448         for (ii = 0; ii < connector->count_modes; ++ii)
449         {
450                 if (drmOutputAddMode(output, &connector->modes[ii]))
451                         goto err_free;
452         }
453
454         if (connector->count_modes == 0)
455         {
456                 if (drmOutputAddMode(output, &builtin_800x480_for_Crossville))
457                         goto err_free;
458         }
459
460         drmMode = wl_container_of(output->modeList.next, drmMode, link);
461         output->currentMode = drmMode;
462         drmMode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
463
464         output->surface = gbm_surface_create(m_gbm,
465                                                        output->currentMode->width,
466                                                        output->currentMode->height,
467                                                        GBM_BO_FORMAT_XRGB8888,
468                                                        GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
469         if (!output->surface) {
470                 LOG_ERROR("DrmGLESGraphicSystem", "failed to create gbm surface.");
471                 goto err_gbm_surface;
472         }
473
474         output->eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig,
475                 (EGLNativeWindowType)output->surface, NULL);
476         if (output->eglSurface == EGL_NO_SURFACE) {
477                 LOG_ERROR("DrmGLESGraphicSystem", "failed to create egl surface.");
478                 goto err_egl_surface;
479         }
480
481         output->screenID = (uint)wl_list_length(&m_outputList);
482         m_currentOutput = output;
483
484         wl_list_insert(m_outputList.prev, &output->link);
485
486         LOG_DEBUG("DrmGLESGraphicSystem", "createOutputForConnector OUT (NORMAL)");
487         return 0;
488
489 err_egl_surface:
490         if (output->eglSurface)
491                 eglDestroySurface(m_eglDisplay, output->eglSurface);
492
493 err_gbm_surface:
494         if (output->surface)
495                 gbm_surface_destroy(output->surface);
496
497 err_free:
498         drmModeFreeCrtc(output->orgCrtc);
499         m_crtcAllocator &= ~(1 << output->crtcID);
500         m_connectorAllocator &= ~(1 << output->connectorID);
501
502         free(output);
503         LOG_DEBUG("DrmGLESGraphicSystem", "createOutputForConnector OUT (ERROR)");
504         return -1;
505 }
506
507 int DrmGLESGraphicSystem::drmOutputAddMode(struct DrmOutput* output, drmModeModeInfo* info)
508 {
509         struct DrmMode* mode;
510
511         mode = (struct DrmMode*)malloc(sizeof *mode);
512         if (mode == NULL)
513                 return -1;
514
515         mode->flags    = 0;
516         mode->width    = info->hdisplay;
517         mode->height   = info->vdisplay;
518         mode->refresh  = info->vrefresh;
519         mode->modeInfo = *info;
520         wl_list_insert(output->modeList.prev, &mode->link);
521
522         return 0;
523 }
524
525 int DrmGLESGraphicSystem::drmOutputPrepareRender(struct DrmOutput* output)
526 {
527         if (!eglMakeCurrent(m_eglDisplay, output->eglSurface, output->eglSurface, m_eglContext))
528         {
529                 LOG_ERROR("DrmGLESGraphicSystem", "failed to make current.");
530                 return -1;
531         }
532         return 0;
533 }
534
535 void DrmGLESGraphicSystem::swapBuffers()
536 {
537     LOG_DEBUG("DrmGLESGraphicSystem", "swapBuffers IN");
538
539         struct DrmOutput* output = NULL;
540
541         wl_list_for_each(output, &m_outputList, link)
542         {
543                 if (output != m_currentOutput)
544                 {
545                         continue;
546                 }
547
548                 if (!output->next)
549                 {
550                         eglSwapBuffers(m_eglDisplay, output->eglSurface);
551                         struct gbm_bo *bo = gbm_surface_lock_front_buffer(output->surface);
552                         if (!bo)
553                                 break;
554
555                         output->next = drmFbGetFromBo(bo, m_fdDev, output);
556                         if (!output->next)
557                         {
558                                 gbm_surface_release_buffer(output->surface, bo);
559                                 break;
560                         }
561                 }
562
563                 if (!output->current)
564                 {
565                         if (drmModeSetCrtc(m_fdDev, output->crtcID,
566                                                                  output->next->fbId, 0, 0,
567                                                                  &output->connectorID, 1,
568                                                                  &output->currentMode->modeInfo))
569                         {
570                                 LOG_ERROR("DrmGLESGraphicSystem", "failed to set mode in swapBuffers.");
571                                 break;
572                         }
573                 }
574
575                 if (drmModePageFlip(m_fdDev, output->crtcID, output->next->fbId,
576                         DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
577                 {
578                         LOG_ERROR("DrmGLESGraphicSystem", "queueing pageflip failed");
579                         break;
580                 }
581
582                 output->pageFlipPending = 1;
583                 break;
584         }
585
586     LOG_DEBUG("DrmGLESGraphicSystem", "swapBuffers OUT");
587 }