[release] version bump to 13.0 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   SetVSyncImpl(m_iVSyncMode);
310   return true;
311 }
312
313 bool CWinSystemEGL::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
314 {
315   CreateNewWindow("", fullScreen, res, NULL);
316   CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight, fullScreen, res.fRefreshRate);
317   SetVSyncImpl(m_iVSyncMode);
318   return true;
319 }
320
321 void CWinSystemEGL::UpdateResolutions()
322 {
323   CWinSystemBase::UpdateResolutions();
324
325   RESOLUTION_INFO resDesktop, curDisplay;
326   std::vector<RESOLUTION_INFO> resolutions;
327
328   if (!m_egl->ProbeResolutions(resolutions) || resolutions.empty())
329   {
330     CLog::Log(LOGWARNING, "%s: ProbeResolutions failed. Trying safe default.",__FUNCTION__);
331
332     RESOLUTION_INFO fallback;
333     if (m_egl->GetPreferredResolution(&fallback))
334     {
335       resolutions.push_back(fallback);
336     }
337     else
338     {
339       CLog::Log(LOGERROR, "%s: Fatal Error, GetPreferredResolution failed",__FUNCTION__);
340       return;
341     }
342   }
343
344   /* ProbeResolutions includes already all resolutions.
345    * Only get desktop resolution so we can replace xbmc's desktop res
346    */
347   if (m_egl->GetNativeResolution(&curDisplay))
348     resDesktop = curDisplay;
349
350
351   RESOLUTION ResDesktop = RES_INVALID;
352   RESOLUTION res_index  = RES_DESKTOP;
353
354   for (size_t i = 0; i < resolutions.size(); i++)
355   {
356     // if this is a new setting,
357     // create a new empty setting to fill in.
358     if ((int)CDisplaySettings::Get().ResolutionInfoSize() <= res_index)
359     {
360       RESOLUTION_INFO res;
361       CDisplaySettings::Get().AddResolutionInfo(res);
362     }
363
364     g_graphicsContext.ResetOverscan(resolutions[i]);
365     CDisplaySettings::Get().GetResolutionInfo(res_index) = resolutions[i];
366
367     CLog::Log(LOGNOTICE, "Found resolution %d x %d for display %d with %d x %d%s @ %f Hz\n",
368       resolutions[i].iWidth,
369       resolutions[i].iHeight,
370       resolutions[i].iScreen,
371       resolutions[i].iScreenWidth,
372       resolutions[i].iScreenHeight,
373       resolutions[i].dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "",
374       resolutions[i].fRefreshRate);
375
376     if(resDesktop.iWidth == resolutions[i].iWidth &&
377        resDesktop.iHeight == resolutions[i].iHeight &&
378        resDesktop.iScreenWidth == resolutions[i].iScreenWidth &&
379        resDesktop.iScreenHeight == resolutions[i].iScreenHeight &&
380        (resDesktop.dwFlags & D3DPRESENTFLAG_MODEMASK) == (resolutions[i].dwFlags & D3DPRESENTFLAG_MODEMASK) &&
381        resDesktop.fRefreshRate == resolutions[i].fRefreshRate)
382     {
383       ResDesktop = res_index;
384     }
385
386     res_index = (RESOLUTION)((int)res_index + 1);
387   }
388
389   // swap desktop index for desktop res if available
390   if (ResDesktop != RES_INVALID)
391   {
392     CLog::Log(LOGNOTICE, "Found (%dx%d%s@%f) at %d, setting to RES_DESKTOP at %d",
393       resDesktop.iWidth, resDesktop.iHeight,
394       resDesktop.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "",
395       resDesktop.fRefreshRate,
396       (int)ResDesktop, (int)RES_DESKTOP);
397
398     RESOLUTION_INFO desktop = CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP);
399     CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP) = CDisplaySettings::Get().GetResolutionInfo(ResDesktop);
400     CDisplaySettings::Get().GetResolutionInfo(ResDesktop) = desktop;
401   }
402 }
403
404 bool CWinSystemEGL::IsExtSupported(const char* extension)
405 {
406   std::string name;
407
408   name  = " ";
409   name += extension;
410   name += " ";
411
412   return (m_extensions.find(name) != std::string::npos || CRenderSystemGLES::IsExtSupported(extension));
413 }
414
415 bool CWinSystemEGL::PresentRenderImpl(const CDirtyRegionList &dirty)
416 {
417   m_egl->SwapBuffers(m_display, m_surface);
418   return true;
419 }
420
421 void CWinSystemEGL::SetVSyncImpl(bool enable)
422 {
423   m_iVSyncMode = enable ? 10:0;
424   if (!m_egl->SetVSync(m_display, enable))
425   {
426     m_iVSyncMode = 0;
427     CLog::Log(LOGERROR, "%s,Could not set egl vsync", __FUNCTION__);
428   }
429 }
430
431 void CWinSystemEGL::ShowOSMouse(bool show)
432 {
433 }
434
435 bool CWinSystemEGL::HasCursor()
436 {
437 #ifdef TARGET_ANDROID
438   return false;
439 #else
440   return true;
441 #endif
442 }
443
444 void CWinSystemEGL::NotifyAppActiveChange(bool bActivated)
445 {
446 }
447
448 bool CWinSystemEGL::Minimize()
449 {
450   Hide();
451   return true;
452 }
453
454 bool CWinSystemEGL::Restore()
455 {
456   Show(true);
457   return false;
458 }
459
460 bool CWinSystemEGL::Hide()
461 {
462   return m_egl->ShowWindow(false);
463 }
464
465 bool CWinSystemEGL::Show(bool raise)
466 {
467   return m_egl->ShowWindow(true);
468 }
469
470 EGLDisplay CWinSystemEGL::GetEGLDisplay()
471 {
472   return m_display;
473 }
474
475 EGLContext CWinSystemEGL::GetEGLContext()
476 {
477   return m_context;
478 }
479
480 EGLConfig CWinSystemEGL::GetEGLConfig()
481 {
482   return m_config;
483 }
484
485 // the logic in this function should match whether CBaseRenderer::FindClosestResolution picks a 3D mode
486 bool CWinSystemEGL::Support3D(int width, int height, uint32_t mode) const
487 {
488   RESOLUTION_INFO &curr = CDisplaySettings::Get().GetResolutionInfo(g_graphicsContext.GetVideoResolution());
489
490   // if we are using automatic hdmi mode switching
491   if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
492   {
493     int searchWidth = curr.iScreenWidth;
494     int searchHeight = curr.iScreenHeight;
495
496     // only search the custom resolutions
497     for (unsigned int i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
498     {
499       RESOLUTION_INFO res = CDisplaySettings::Get().GetResolutionInfo(i);
500       if(res.iScreenWidth == searchWidth && res.iScreenHeight == searchHeight && (res.dwFlags & mode))
501         return true;
502     }
503   }
504   // otherwise just consider current mode
505   else
506   {
507      if (curr.dwFlags & mode)
508        return true;
509   }
510
511   return false;
512 }
513
514 bool CWinSystemEGL::ClampToGUIDisplayLimits(int &width, int &height)
515 {
516   width = width > m_nWidth ? m_nWidth : width;
517   height = height > m_nHeight ? m_nHeight : height;
518   return true;
519 }
520
521 #endif