Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / windowing / egl / WinSystemEGL.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "system.h"
21
22 #ifdef HAS_EGL
23
24 #include "WinSystemEGL.h"
25 #include "filesystem/SpecialProtocol.h"
26 #include "guilib/GraphicContext.h"
27 #include "settings/DisplaySettings.h"
28 #include "guilib/IDirtyRegionSolver.h"
29 #include "settings/AdvancedSettings.h"
30 #include "settings/Settings.h"
31 #include "settings/DisplaySettings.h"
32 #include "utils/log.h"
33 #include "EGLWrapper.h"
34 #include "EGLQuirks.h"
35 #include <vector>
36 ////////////////////////////////////////////////////////////////////////////////////////////
37 CWinSystemEGL::CWinSystemEGL() : CWinSystemBase()
38 {
39   m_eWindowSystem = WINDOW_SYSTEM_EGL;
40
41   m_displayWidth      = 0;
42   m_displayHeight     = 0;
43
44   m_display           = EGL_NO_DISPLAY;
45   m_surface           = EGL_NO_SURFACE;
46   m_context           = EGL_NO_CONTEXT;
47   m_config            = NULL;
48
49   m_egl               = NULL;
50   m_iVSyncMode        = 0;
51 }
52
53 CWinSystemEGL::~CWinSystemEGL()
54 {
55   if (m_egl)
56   {
57     DestroyWindowSystem();
58     delete m_egl;
59   }
60 }
61
62 bool CWinSystemEGL::InitWindowSystem()
63 {
64   RESOLUTION_INFO preferred_resolution;
65   if (!m_egl)
66     m_egl = new CEGLWrapper;
67
68   if (!m_egl)
69   {
70     CLog::Log(LOGERROR, "%s: EGL not in a good state",__FUNCTION__);
71     return false;
72   }
73
74   if (!m_egl->Initialize("auto"))
75   {
76     CLog::Log(LOGERROR, "%s: Could not initialize",__FUNCTION__);
77     return false;
78   }
79
80   CLog::Log(LOGNOTICE, "%s: Using EGL Implementation: %s",__FUNCTION__,m_egl->GetNativeName().c_str());
81
82   if (!m_egl->CreateNativeDisplay())
83   {
84     CLog::Log(LOGERROR, "%s: Could not get native display",__FUNCTION__);
85     return false;
86   }
87
88   if (!m_egl->CreateNativeWindow())
89   {
90     CLog::Log(LOGERROR, "%s: Could not get native window",__FUNCTION__);
91     return false;
92   }
93
94   if (!m_egl->InitDisplay(&m_display))
95   {
96     CLog::Log(LOGERROR, "%s: Could not create display",__FUNCTION__);
97     return false;
98   }
99
100   EGLint surface_type = EGL_WINDOW_BIT;
101   // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates
102   if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION ||
103       g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION)
104     surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
105
106   EGLint configAttrs [] = {
107         EGL_RED_SIZE,        8,
108         EGL_GREEN_SIZE,      8,
109         EGL_BLUE_SIZE,       8,
110         EGL_ALPHA_SIZE,      8,
111         EGL_DEPTH_SIZE,     16,
112         EGL_STENCIL_SIZE,    0,
113         EGL_SAMPLE_BUFFERS,  0,
114         EGL_SAMPLES,         0,
115         EGL_SURFACE_TYPE,    surface_type,
116         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
117         EGL_NONE
118   };
119
120   if (!m_egl->ChooseConfig(m_display, configAttrs, &m_config))
121   {
122     CLog::Log(LOGERROR, "%s: Could not find a compatible configuration",__FUNCTION__);
123     return false;
124   }
125
126   // Some platforms require a surface before we can probe the resolution.
127   // Create the window here, then the creation in CreateNewWindow() will be skipped.
128   int quirks;
129   m_egl->GetQuirks(&quirks);
130   if (quirks & EGL_QUIRK_NEED_WINDOW_FOR_RES)
131   {
132     RESOLUTION_INFO temp;
133     CreateWindow(temp);
134   }
135
136   m_extensions = m_egl->GetExtensions(m_display);
137   return CWinSystemBase::InitWindowSystem();
138 }
139
140 bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
141 {
142   if (!m_egl)
143   {
144     CLog::Log(LOGERROR, "CWinSystemEGL::CreateWindow no EGL!");
145     return false;
146   }
147
148   if(m_egl)
149     m_egl->SetNativeResolution(res);
150
151   if (!m_egl->CreateSurface(m_display, m_config, &m_surface))
152   {
153     CLog::Log(LOGNOTICE, "%s: Could not create a surface. Trying with a fresh Native Window.",__FUNCTION__);
154     m_egl->DestroyNativeWindow();
155     if (!m_egl->CreateNativeWindow())
156     {
157       CLog::Log(LOGERROR, "%s: Could not get native window",__FUNCTION__);
158       return false;
159     }
160
161     if (!m_egl->CreateSurface(m_display, m_config, &m_surface))
162     {
163       CLog::Log(LOGERROR, "%s: Could not create surface",__FUNCTION__);
164       return false;
165     }
166   }
167
168   /* The intel driver on wayland is broken and always returns a surface
169    * size of -1, -1. Work around it for now */
170   if (m_egl->TrustSurfaceSize())
171   {
172     int width = 0, height = 0;
173     if (!m_egl->GetSurfaceSize(m_display, m_surface, &width, &height))
174     {
175       CLog::Log(LOGERROR, "%s: Surface is invalid",__FUNCTION__);
176       return false;
177     }
178     CLog::Log(LOGDEBUG, "%s: Created surface of size %ix%i",__FUNCTION__, width, height);
179   }
180   else
181     CLog::Log(LOGDEBUG, "%s: Cannot reliably get surface size with this backend",__FUNCTION__);
182
183   EGLint contextAttrs[] =
184   {
185     EGL_CONTEXT_CLIENT_VERSION, 2,
186     EGL_NONE
187   };
188
189   if (!m_egl->BindAPI(EGL_OPENGL_ES_API))
190   {
191     CLog::Log(LOGERROR, "%s: Could not bind %i api",__FUNCTION__, EGL_OPENGL_ES_API);
192     return false;
193   }
194
195   if (m_context == EGL_NO_CONTEXT)
196   {
197     if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context))
198     {
199       CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__);
200       return false;
201     }
202   }
203
204   if (!m_egl->BindContext(m_display, m_surface, m_context))
205   {
206     CLog::Log(LOGERROR, "%s: Could not bind to context",__FUNCTION__);
207     return false;
208   }
209
210   // for the non-trivial dirty region modes, we need the EGL buffer to be preserved across updates
211   if (g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_COST_REDUCTION ||
212       g_advancedSettings.m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_UNION)
213   {
214     if (!m_egl->SurfaceAttrib(m_display, m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED))
215       CLog::Log(LOGDEBUG, "%s: Could not set EGL_SWAP_BEHAVIOR",__FUNCTION__);
216   }
217
218   m_bWindowCreated = true;
219
220   return true;
221 }
222
223 bool CWinSystemEGL::DestroyWindowSystem()
224 {
225   if (!m_egl)
226     return true;
227
228   DestroyWindow();
229
230   if (m_context != EGL_NO_CONTEXT)
231     m_egl->DestroyContext(m_display, m_context);
232   m_context = EGL_NO_CONTEXT;
233
234   if (m_display != EGL_NO_DISPLAY)
235     m_egl->DestroyDisplay(m_display);
236   m_display = EGL_NO_DISPLAY;
237
238   m_egl->DestroyNativeWindow();
239
240   m_egl->DestroyNativeDisplay();
241
242   m_egl->Destroy();
243   delete m_egl;
244   m_egl = NULL;
245
246   return true;
247 }
248
249 bool CWinSystemEGL::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
250 {
251   RESOLUTION_INFO current_resolution;
252   current_resolution.iWidth = current_resolution.iHeight = 0;
253
254   m_nWidth        = res.iWidth;    
255   m_nHeight       = res.iHeight;
256   m_displayWidth  = res.iScreenWidth;
257   m_displayHeight = res.iScreenHeight;
258   m_fRefreshRate  = res.fRefreshRate;
259
260   if ((m_bWindowCreated && m_egl && m_egl->GetNativeResolution(&current_resolution)) &&
261     current_resolution.iWidth == res.iWidth && current_resolution.iHeight == res.iHeight &&
262     current_resolution.iScreenWidth == res.iScreenWidth && current_resolution.iScreenHeight == res.iScreenHeight &&
263     m_bFullScreen == fullScreen && current_resolution.fRefreshRate == res.fRefreshRate &&
264     (current_resolution.dwFlags & D3DPRESENTFLAG_MODEMASK) == (res.dwFlags & D3DPRESENTFLAG_MODEMASK))
265   {
266     CLog::Log(LOGDEBUG, "CWinSystemEGL::CreateNewWindow: No need to create a new window");
267     return true;
268   }
269
270   m_bFullScreen   = fullScreen;
271   // Destroy any existing window
272   if (m_surface != EGL_NO_SURFACE)
273     DestroyWindow();
274
275   // If we previously destroyed an existing window we need to create a new one
276   // (otherwise this is taken care of by InitWindowSystem())
277   if (!CreateWindow(res))
278   {
279     CLog::Log(LOGERROR, "%s: Could not create new window",__FUNCTION__);
280     return false;
281   }
282   Show();
283
284   return true;
285 }
286
287 bool CWinSystemEGL::DestroyWindow()
288 {
289   if (!m_egl)
290     return false;
291
292   m_egl->ReleaseContext(m_display);
293   if (m_surface != EGL_NO_SURFACE)
294     m_egl->DestroySurface(m_surface, m_display);
295
296   int quirks;
297   m_egl->GetQuirks(&quirks);
298   if (quirks & EGL_QUIRK_DESTROY_NATIVE_WINDOW_WITH_SURFACE)
299     m_egl->DestroyNativeWindow();
300
301   m_surface = EGL_NO_SURFACE;
302   m_bWindowCreated = false;
303   return true;
304 }
305
306 bool CWinSystemEGL::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
307 {
308   CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight, true, 0);
309   int vsync_mode = CSettings::Get().GetInt("videoscreen.vsync");
310   if (vsync_mode != VSYNC_DRIVER)
311     SetVSyncImpl(m_iVSyncMode);
312   return true;
313 }
314
315 bool CWinSystemEGL::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
316 {
317   CreateNewWindow("", fullScreen, res, NULL);
318   CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight, fullScreen, res.fRefreshRate);
319   int vsync_mode = CSettings::Get().GetInt("videoscreen.vsync");
320   if (vsync_mode != VSYNC_DRIVER)
321     SetVSyncImpl(m_iVSyncMode);
322   return true;
323 }
324
325 void CWinSystemEGL::UpdateResolutions()
326 {
327   CWinSystemBase::UpdateResolutions();
328
329   RESOLUTION_INFO resDesktop, curDisplay;
330   std::vector<RESOLUTION_INFO> resolutions;
331
332   if (!m_egl->ProbeResolutions(resolutions) || resolutions.empty())
333   {
334     CLog::Log(LOGWARNING, "%s: ProbeResolutions failed. Trying safe default.",__FUNCTION__);
335
336     RESOLUTION_INFO fallback;
337     if (m_egl->GetPreferredResolution(&fallback))
338     {
339       resolutions.push_back(fallback);
340     }
341     else
342     {
343       CLog::Log(LOGERROR, "%s: Fatal Error, GetPreferredResolution failed",__FUNCTION__);
344       return;
345     }
346   }
347
348   /* ProbeResolutions includes already all resolutions.
349    * Only get desktop resolution so we can replace xbmc's desktop res
350    */
351   if (m_egl->GetNativeResolution(&curDisplay))
352     resDesktop = curDisplay;
353
354
355   RESOLUTION ResDesktop = RES_INVALID;
356   RESOLUTION res_index  = RES_DESKTOP;
357
358   for (size_t i = 0; i < resolutions.size(); i++)
359   {
360     // if this is a new setting,
361     // create a new empty setting to fill in.
362     if ((int)CDisplaySettings::Get().ResolutionInfoSize() <= res_index)
363     {
364       RESOLUTION_INFO res;
365       CDisplaySettings::Get().AddResolutionInfo(res);
366     }
367
368     g_graphicsContext.ResetOverscan(resolutions[i]);
369     CDisplaySettings::Get().GetResolutionInfo(res_index) = resolutions[i];
370
371     CLog::Log(LOGNOTICE, "Found resolution %d x %d for display %d with %d x %d%s @ %f Hz\n",
372       resolutions[i].iWidth,
373       resolutions[i].iHeight,
374       resolutions[i].iScreen,
375       resolutions[i].iScreenWidth,
376       resolutions[i].iScreenHeight,
377       resolutions[i].dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "",
378       resolutions[i].fRefreshRate);
379
380     if(resDesktop.iWidth == resolutions[i].iWidth &&
381        resDesktop.iHeight == resolutions[i].iHeight &&
382        resDesktop.iScreenWidth == resolutions[i].iScreenWidth &&
383        resDesktop.iScreenHeight == resolutions[i].iScreenHeight &&
384        (resDesktop.dwFlags & D3DPRESENTFLAG_MODEMASK) == (resolutions[i].dwFlags & D3DPRESENTFLAG_MODEMASK) &&
385        resDesktop.fRefreshRate == resolutions[i].fRefreshRate)
386     {
387       ResDesktop = res_index;
388     }
389
390     res_index = (RESOLUTION)((int)res_index + 1);
391   }
392
393   // swap desktop index for desktop res if available
394   if (ResDesktop != RES_INVALID)
395   {
396     CLog::Log(LOGNOTICE, "Found (%dx%d%s@%f) at %d, setting to RES_DESKTOP at %d",
397       resDesktop.iWidth, resDesktop.iHeight,
398       resDesktop.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "",
399       resDesktop.fRefreshRate,
400       (int)ResDesktop, (int)RES_DESKTOP);
401
402     RESOLUTION_INFO desktop = CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP);
403     CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP) = CDisplaySettings::Get().GetResolutionInfo(ResDesktop);
404     CDisplaySettings::Get().GetResolutionInfo(ResDesktop) = desktop;
405   }
406 }
407
408 bool CWinSystemEGL::IsExtSupported(const char* extension)
409 {
410   std::string name;
411
412   name  = " ";
413   name += extension;
414   name += " ";
415
416   return (m_extensions.find(name) != std::string::npos || CRenderSystemGLES::IsExtSupported(extension));
417 }
418
419 bool CWinSystemEGL::PresentRenderImpl(const CDirtyRegionList &dirty)
420 {
421   m_egl->SwapBuffers(m_display, m_surface);
422   return true;
423 }
424
425 void CWinSystemEGL::SetVSyncImpl(bool enable)
426 {
427   m_iVSyncMode = enable ? 10:0;
428   if (!m_egl->SetVSync(m_display, enable))
429   {
430     m_iVSyncMode = 0;
431     CLog::Log(LOGERROR, "%s,Could not set egl vsync", __FUNCTION__);
432   }
433 }
434
435 void CWinSystemEGL::ShowOSMouse(bool show)
436 {
437 }
438
439 bool CWinSystemEGL::HasCursor()
440 {
441 #ifdef TARGET_ANDROID
442   return false;
443 #else
444   return true;
445 #endif
446 }
447
448 void CWinSystemEGL::NotifyAppActiveChange(bool bActivated)
449 {
450 }
451
452 bool CWinSystemEGL::Minimize()
453 {
454   Hide();
455   return true;
456 }
457
458 bool CWinSystemEGL::Restore()
459 {
460   Show(true);
461   return false;
462 }
463
464 bool CWinSystemEGL::Hide()
465 {
466   return m_egl->ShowWindow(false);
467 }
468
469 bool CWinSystemEGL::Show(bool raise)
470 {
471   return m_egl->ShowWindow(true);
472 }
473
474 EGLDisplay CWinSystemEGL::GetEGLDisplay()
475 {
476   return m_display;
477 }
478
479 EGLContext CWinSystemEGL::GetEGLContext()
480 {
481   return m_context;
482 }
483
484 EGLConfig CWinSystemEGL::GetEGLConfig()
485 {
486   return m_config;
487 }
488
489 // the logic in this function should match whether CBaseRenderer::FindClosestResolution picks a 3D mode
490 bool CWinSystemEGL::Support3D(int width, int height, uint32_t mode) const
491 {
492   RESOLUTION_INFO &curr = CDisplaySettings::Get().GetResolutionInfo(g_graphicsContext.GetVideoResolution());
493
494   // if we are using automatic hdmi mode switching
495   if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
496   {
497     int searchWidth = curr.iScreenWidth;
498     int searchHeight = curr.iScreenHeight;
499
500     // only search the custom resolutions
501     for (unsigned int i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
502     {
503       RESOLUTION_INFO res = CDisplaySettings::Get().GetResolutionInfo(i);
504       if(res.iScreenWidth == searchWidth && res.iScreenHeight == searchHeight && (res.dwFlags & mode))
505         return true;
506     }
507   }
508   // otherwise just consider current mode
509   else
510   {
511      if (curr.dwFlags & mode)
512        return true;
513   }
514
515   return false;
516 }
517
518 bool CWinSystemEGL::ClampToGUIDisplayLimits(int &width, int &height)
519 {
520   width = width > m_nWidth ? m_nWidth : width;
521   height = height > m_nHeight ? m_nHeight : height;
522   return true;
523 }
524
525 #endif