2 * Copyright (C) 2011-2013 Team XBMC
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)
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.
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/>.
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"
36 ////////////////////////////////////////////////////////////////////////////////////////////
37 CWinSystemEGL::CWinSystemEGL() : CWinSystemBase()
39 m_eWindowSystem = WINDOW_SYSTEM_EGL;
44 m_display = EGL_NO_DISPLAY;
45 m_surface = EGL_NO_SURFACE;
46 m_context = EGL_NO_CONTEXT;
53 CWinSystemEGL::~CWinSystemEGL()
57 DestroyWindowSystem();
62 bool CWinSystemEGL::InitWindowSystem()
64 RESOLUTION_INFO preferred_resolution;
66 m_egl = new CEGLWrapper;
70 CLog::Log(LOGERROR, "%s: EGL not in a good state",__FUNCTION__);
74 if (!m_egl->Initialize("auto"))
76 CLog::Log(LOGERROR, "%s: Could not initialize",__FUNCTION__);
80 CLog::Log(LOGNOTICE, "%s: Using EGL Implementation: %s",__FUNCTION__,m_egl->GetNativeName().c_str());
82 if (!m_egl->CreateNativeDisplay())
84 CLog::Log(LOGERROR, "%s: Could not get native display",__FUNCTION__);
88 if (!m_egl->CreateNativeWindow())
90 CLog::Log(LOGERROR, "%s: Could not get native window",__FUNCTION__);
94 if (!m_egl->InitDisplay(&m_display))
96 CLog::Log(LOGERROR, "%s: Could not create display",__FUNCTION__);
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;
106 EGLint configAttrs [] = {
113 EGL_SAMPLE_BUFFERS, 0,
115 EGL_SURFACE_TYPE, surface_type,
116 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
120 if (!m_egl->ChooseConfig(m_display, configAttrs, &m_config))
122 CLog::Log(LOGERROR, "%s: Could not find a compatible configuration",__FUNCTION__);
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.
129 m_egl->GetQuirks(&quirks);
130 if (quirks & EGL_QUIRK_NEED_WINDOW_FOR_RES)
132 RESOLUTION_INFO temp;
136 m_extensions = m_egl->GetExtensions(m_display);
137 return CWinSystemBase::InitWindowSystem();
140 bool CWinSystemEGL::CreateWindow(RESOLUTION_INFO &res)
144 CLog::Log(LOGERROR, "CWinSystemEGL::CreateWindow no EGL!");
149 m_egl->SetNativeResolution(res);
151 if (!m_egl->CreateSurface(m_display, m_config, &m_surface))
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())
157 CLog::Log(LOGERROR, "%s: Could not get native window",__FUNCTION__);
161 if (!m_egl->CreateSurface(m_display, m_config, &m_surface))
163 CLog::Log(LOGERROR, "%s: Could not create surface",__FUNCTION__);
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())
172 int width = 0, height = 0;
173 if (!m_egl->GetSurfaceSize(m_display, m_surface, &width, &height))
175 CLog::Log(LOGERROR, "%s: Surface is invalid",__FUNCTION__);
178 CLog::Log(LOGDEBUG, "%s: Created surface of size %ix%i",__FUNCTION__, width, height);
181 CLog::Log(LOGDEBUG, "%s: Cannot reliably get surface size with this backend",__FUNCTION__);
183 EGLint contextAttrs[] =
185 EGL_CONTEXT_CLIENT_VERSION, 2,
189 if (!m_egl->BindAPI(EGL_OPENGL_ES_API))
191 CLog::Log(LOGERROR, "%s: Could not bind %i api",__FUNCTION__, EGL_OPENGL_ES_API);
195 if (m_context == EGL_NO_CONTEXT)
197 if (!m_egl->CreateContext(m_display, m_config, contextAttrs, &m_context))
199 CLog::Log(LOGERROR, "%s: Could not create context",__FUNCTION__);
204 if (!m_egl->BindContext(m_display, m_surface, m_context))
206 CLog::Log(LOGERROR, "%s: Could not bind to context",__FUNCTION__);
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)
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__);
218 m_bWindowCreated = true;
223 bool CWinSystemEGL::DestroyWindowSystem()
230 if (m_context != EGL_NO_CONTEXT)
231 m_egl->DestroyContext(m_display, m_context);
232 m_context = EGL_NO_CONTEXT;
234 if (m_display != EGL_NO_DISPLAY)
235 m_egl->DestroyDisplay(m_display);
236 m_display = EGL_NO_DISPLAY;
238 m_egl->DestroyNativeWindow();
240 m_egl->DestroyNativeDisplay();
249 bool CWinSystemEGL::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
251 RESOLUTION_INFO current_resolution;
252 current_resolution.iWidth = current_resolution.iHeight = 0;
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;
260 if ((m_bWindowCreated && m_egl && m_egl->GetNativeResolution(¤t_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))
266 CLog::Log(LOGDEBUG, "CWinSystemEGL::CreateNewWindow: No need to create a new window");
270 m_bFullScreen = fullScreen;
271 // Destroy any existing window
272 if (m_surface != EGL_NO_SURFACE)
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))
279 CLog::Log(LOGERROR, "%s: Could not create new window",__FUNCTION__);
287 bool CWinSystemEGL::DestroyWindow()
292 m_egl->ReleaseContext(m_display);
293 if (m_surface != EGL_NO_SURFACE)
294 m_egl->DestroySurface(m_surface, m_display);
297 m_egl->GetQuirks(&quirks);
298 if (quirks & EGL_QUIRK_DESTROY_NATIVE_WINDOW_WITH_SURFACE)
299 m_egl->DestroyNativeWindow();
301 m_surface = EGL_NO_SURFACE;
302 m_bWindowCreated = false;
306 bool CWinSystemEGL::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
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);
315 bool CWinSystemEGL::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
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);
325 void CWinSystemEGL::UpdateResolutions()
327 CWinSystemBase::UpdateResolutions();
329 RESOLUTION_INFO resDesktop, curDisplay;
330 std::vector<RESOLUTION_INFO> resolutions;
332 if (!m_egl->ProbeResolutions(resolutions) || resolutions.empty())
334 CLog::Log(LOGWARNING, "%s: ProbeResolutions failed. Trying safe default.",__FUNCTION__);
336 RESOLUTION_INFO fallback;
337 if (m_egl->GetPreferredResolution(&fallback))
339 resolutions.push_back(fallback);
343 CLog::Log(LOGERROR, "%s: Fatal Error, GetPreferredResolution failed",__FUNCTION__);
348 /* ProbeResolutions includes already all resolutions.
349 * Only get desktop resolution so we can replace xbmc's desktop res
351 if (m_egl->GetNativeResolution(&curDisplay))
352 resDesktop = curDisplay;
355 RESOLUTION ResDesktop = RES_INVALID;
356 RESOLUTION res_index = RES_DESKTOP;
358 for (size_t i = 0; i < resolutions.size(); i++)
360 // if this is a new setting,
361 // create a new empty setting to fill in.
362 if ((int)CDisplaySettings::Get().ResolutionInfoSize() <= res_index)
365 CDisplaySettings::Get().AddResolutionInfo(res);
368 g_graphicsContext.ResetOverscan(resolutions[i]);
369 CDisplaySettings::Get().GetResolutionInfo(res_index) = resolutions[i];
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);
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)
387 ResDesktop = res_index;
390 res_index = (RESOLUTION)((int)res_index + 1);
393 // swap desktop index for desktop res if available
394 if (ResDesktop != RES_INVALID)
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);
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;
408 bool CWinSystemEGL::IsExtSupported(const char* extension)
416 return (m_extensions.find(name) != std::string::npos || CRenderSystemGLES::IsExtSupported(extension));
419 bool CWinSystemEGL::PresentRenderImpl(const CDirtyRegionList &dirty)
421 m_egl->SwapBuffers(m_display, m_surface);
425 void CWinSystemEGL::SetVSyncImpl(bool enable)
427 m_iVSyncMode = enable ? 10:0;
428 if (!m_egl->SetVSync(m_display, enable))
431 CLog::Log(LOGERROR, "%s,Could not set egl vsync", __FUNCTION__);
435 void CWinSystemEGL::ShowOSMouse(bool show)
439 bool CWinSystemEGL::HasCursor()
441 #ifdef TARGET_ANDROID
448 void CWinSystemEGL::NotifyAppActiveChange(bool bActivated)
452 bool CWinSystemEGL::Minimize()
458 bool CWinSystemEGL::Restore()
464 bool CWinSystemEGL::Hide()
466 return m_egl->ShowWindow(false);
469 bool CWinSystemEGL::Show(bool raise)
471 return m_egl->ShowWindow(true);
474 EGLDisplay CWinSystemEGL::GetEGLDisplay()
479 EGLContext CWinSystemEGL::GetEGLContext()
484 EGLConfig CWinSystemEGL::GetEGLConfig()
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
492 RESOLUTION_INFO &curr = CDisplaySettings::Get().GetResolutionInfo(g_graphicsContext.GetVideoResolution());
494 // if we are using automatic hdmi mode switching
495 if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
497 int searchWidth = curr.iScreenWidth;
498 int searchHeight = curr.iScreenHeight;
500 // only search the custom resolutions
501 for (unsigned int i = (int)RES_DESKTOP; i < CDisplaySettings::Get().ResolutionInfoSize(); i++)
503 RESOLUTION_INFO res = CDisplaySettings::Get().GetResolutionInfo(i);
504 if(res.iScreenWidth == searchWidth && res.iScreenHeight == searchHeight && (res.dwFlags & mode))
508 // otherwise just consider current mode
511 if (curr.dwFlags & mode)
518 bool CWinSystemEGL::ClampToGUIDisplayLimits(int &width, int &height)
520 width = width > m_nWidth ? m_nWidth : width;
521 height = height > m_nHeight ? m_nHeight : height;