2 * Copyright (C) 2005-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/>.
26 #include "guilib/GraphicContext.h"
27 #include "settings/AdvancedSettings.h"
28 #include "RenderSystemGLES.h"
29 #include "guilib/MatrixGLES.h"
30 #include "windowing/WindowingFactory.h"
31 #include "utils/log.h"
32 #include "utils/GLUtils.h"
33 #include "utils/TimeUtils.h"
34 #include "utils/SystemInfo.h"
35 #include "utils/MathUtils.h"
37 static const char* ShaderNames[SM_ESHADERCOUNT] =
38 {"guishader_frag_default.glsl",
39 "guishader_frag_texture.glsl",
40 "guishader_frag_multi.glsl",
41 "guishader_frag_fonts.glsl",
42 "guishader_frag_texture_noblend.glsl",
43 "guishader_frag_multi_blendcolor.glsl",
44 "guishader_frag_rgba.glsl",
45 "guishader_frag_rgba_oes.glsl",
46 "guishader_frag_rgba_blendcolor.glsl"
49 CRenderSystemGLES::CRenderSystemGLES()
52 , m_method(SM_DEFAULT)
54 m_enumRenderingSystem = RENDERING_SYSTEM_OPENGLES;
57 CRenderSystemGLES::~CRenderSystemGLES()
61 bool CRenderSystemGLES::InitRenderSystem()
65 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
67 m_maxTextureSize = maxTextureSize;
75 // Get the GLES version number
76 m_RenderVersionMajor = 0;
77 m_RenderVersionMinor = 0;
79 const char* ver = (const char*)glGetString(GL_VERSION);
82 sscanf(ver, "%d.%d", &m_RenderVersionMajor, &m_RenderVersionMinor);
83 if (!m_RenderVersionMajor)
84 sscanf(ver, "%*s %*s %d.%d", &m_RenderVersionMajor, &m_RenderVersionMinor);
85 m_RenderVersion = ver;
88 // Get our driver vendor and renderer
89 m_RenderVendor = (const char*) glGetString(GL_VENDOR);
90 m_RenderRenderer = (const char*) glGetString(GL_RENDERER);
92 m_RenderExtensions = " ";
93 m_RenderExtensions += (const char*) glGetString(GL_EXTENSIONS);
94 m_RenderExtensions += " ";
98 if (IsExtSupported("GL_TEXTURE_NPOT"))
100 m_renderCaps |= RENDER_CAPS_NPOT;
103 if (IsExtSupported("GL_EXT_texture_format_BGRA8888"))
105 m_renderCaps |= RENDER_CAPS_BGRA;
108 if (IsExtSupported("GL_IMG_texture_format_BGRA8888"))
110 m_renderCaps |= RENDER_CAPS_BGRA;
113 if (IsExtSupported("GL_APPLE_texture_format_BGRA8888"))
115 m_renderCaps |= RENDER_CAPS_BGRA_APPLE;
120 m_bRenderCreated = true;
122 InitialiseGUIShader();
127 bool CRenderSystemGLES::ResetRenderSystem(int width, int height, bool fullScreen, float refreshRate)
132 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
133 CalculateMaxTexturesize();
135 CRect rect( 0, 0, width, height );
138 glEnable(GL_SCISSOR_TEST);
140 g_matrices.MatrixMode(MM_PROJECTION);
141 g_matrices.LoadIdentity();
143 g_matrices.Ortho(0.0f, width-1, height-1, 0.0f, -1.0f, 1.0f);
145 g_matrices.MatrixMode(MM_MODELVIEW);
146 g_matrices.LoadIdentity();
148 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
149 glEnable(GL_BLEND); // Turn Blending On
150 glDisable(GL_DEPTH_TEST);
155 bool CRenderSystemGLES::DestroyRenderSystem()
157 CLog::Log(LOGDEBUG, "GUI Shader - Destroying Shader : %p", m_pGUIshader);
161 for (int i = 0; i < SM_ESHADERCOUNT; i++)
165 m_pGUIshader[i]->Free();
166 delete m_pGUIshader[i];
167 m_pGUIshader[i] = NULL;
170 delete[] m_pGUIshader;
175 CDirtyRegionList dirtyRegions;
176 CDirtyRegion dirtyWindow(g_graphicsContext.GetViewWindow());
177 dirtyRegions.push_back(dirtyWindow);
181 PresentRenderImpl(dirtyRegions);
183 m_bRenderCreated = false;
188 bool CRenderSystemGLES::BeginRender()
190 if (!m_bRenderCreated)
196 bool CRenderSystemGLES::EndRender()
198 if (!m_bRenderCreated)
204 bool CRenderSystemGLES::ClearBuffers(color_t color)
206 if (!m_bRenderCreated)
209 float r = GET_R(color) / 255.0f;
210 float g = GET_G(color) / 255.0f;
211 float b = GET_B(color) / 255.0f;
212 float a = GET_A(color) / 255.0f;
214 glClearColor(r, g, b, a);
216 GLbitfield flags = GL_COLOR_BUFFER_BIT;
222 bool CRenderSystemGLES::IsExtSupported(const char* extension)
224 if (strcmp( extension, "GL_EXT_framebuffer_object" ) == 0)
226 // GLES has FBO as a core element, not an extension!
229 else if (strcmp( extension, "GL_TEXTURE_NPOT" ) == 0)
231 // GLES supports non-power-of-two textures as standard.
233 /* Note: The wrap mode can only be GL_CLAMP_TO_EDGE and the minification filter can only be
234 * GL_NEAREST or GL_LINEAR (in other words, not mipmapped). The extension GL_OES_texture_npot
235 * relaxes these restrictions and allows wrap modes of GL_REPEAT and GL_MIRRORED_REPEAT and
236 * also allows npot textures to be mipmapped with the full set of minification filters
246 bool supported = m_RenderExtensions.find(name) != std::string::npos;
247 CLog::Log(LOGDEBUG, "GLES: Extension Support Test - %s %s", extension, supported ? "YES" : "NO");
252 static int64_t abs64(int64_t a)
259 bool CRenderSystemGLES::PresentRender(const CDirtyRegionList &dirty)
261 if (!m_bRenderCreated)
264 if (m_iVSyncMode != 0 && m_iSwapRate != 0)
266 int64_t curr, diff, freq;
267 curr = CurrentHostCounter();
268 freq = CurrentHostFrequency();
270 if(m_iSwapStamp == 0)
273 /* calculate our next swap timestamp */
274 diff = curr - m_iSwapStamp;
275 diff = m_iSwapRate - diff % m_iSwapRate;
276 m_iSwapStamp = curr + diff;
278 /* sleep as close as we can before, assume 1ms precision of sleep *
279 * this should always awake so that we are guaranteed the given *
280 * m_iSwapTime to do our swap */
281 diff = (diff - m_iSwapTime) * 1000 / freq;
286 bool result = PresentRenderImpl(dirty);
288 if (m_iVSyncMode && m_iSwapRate != 0)
291 curr = CurrentHostCounter();
293 diff = curr - m_iSwapStamp;
296 if (abs64(diff - m_iSwapRate) < abs64(diff))
297 CLog::Log(LOGDEBUG, "%s - missed requested swap",__FUNCTION__);
303 void CRenderSystemGLES::SetVSync(bool enable)
305 if (m_bVSync==enable && m_bVsyncInit == true)
308 if (!m_bRenderCreated)
312 CLog::Log(LOGINFO, "GLES: Enabling VSYNC");
314 CLog::Log(LOGINFO, "GLES: Disabling VSYNC");
322 SetVSyncImpl(enable);
327 if (g_advancedSettings.m_ForcedSwapTime != 0.0)
329 /* some hardware busy wait on swap/glfinish, so we must manually sleep to avoid 100% cpu */
330 double rate = g_graphicsContext.GetFPS();
331 if (rate <= 0.0 || rate > 1000.0)
333 CLog::Log(LOGWARNING, "Unable to determine a valid horizontal refresh rate, vsync workaround disabled %.2g", rate);
339 freq = CurrentHostFrequency();
340 m_iSwapRate = (int64_t)((double)freq / rate);
341 m_iSwapTime = (int64_t)(0.001 * g_advancedSettings.m_ForcedSwapTime * freq);
343 CLog::Log(LOGINFO, "GLES: Using artificial vsync sleep with rate %f", rate);
350 CLog::Log(LOGERROR, "GLES: Vertical Blank Syncing unsupported");
352 CLog::Log(LOGINFO, "GLES: Selected vsync mode %d", m_iVSyncMode);
355 void CRenderSystemGLES::CaptureStateBlock()
357 if (!m_bRenderCreated)
360 g_matrices.MatrixMode(MM_PROJECTION);
361 g_matrices.PushMatrix();
362 g_matrices.MatrixMode(MM_TEXTURE);
363 g_matrices.PushMatrix();
364 g_matrices.MatrixMode(MM_MODELVIEW);
365 g_matrices.PushMatrix();
366 glDisable(GL_SCISSOR_TEST); // fixes FBO corruption on Macs
367 glActiveTexture(GL_TEXTURE0);
368 //TODO - NOTE: Only for Screensavers & Visualisations
369 // glColor3f(1.0, 1.0, 1.0);
372 void CRenderSystemGLES::ApplyStateBlock()
374 if (!m_bRenderCreated)
377 g_matrices.MatrixMode(MM_PROJECTION);
378 g_matrices.PopMatrix();
379 g_matrices.MatrixMode(MM_TEXTURE);
380 g_matrices.PopMatrix();
381 g_matrices.MatrixMode(MM_MODELVIEW);
382 g_matrices.PopMatrix();
383 glActiveTexture(GL_TEXTURE0);
385 glEnable(GL_SCISSOR_TEST);
386 glClear(GL_DEPTH_BUFFER_BIT);
389 void CRenderSystemGLES::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight)
391 if (!m_bRenderCreated)
394 g_graphicsContext.BeginPaint();
396 CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
398 float w = (float)m_viewPort[2]*0.5f;
399 float h = (float)m_viewPort[3]*0.5f;
401 g_matrices.MatrixMode(MM_MODELVIEW);
402 g_matrices.LoadIdentity();
403 g_matrices.Translatef(-(w + offset.x), +(h + offset.y), 0);
404 g_matrices.LookAt(0.0, 0.0, -2.0*h, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0);
405 g_matrices.MatrixMode(MM_PROJECTION);
406 g_matrices.LoadIdentity();
407 g_matrices.Frustum( (-w - offset.x)*0.5f, (w - offset.x)*0.5f, (-h + offset.y)*0.5f, (h + offset.y)*0.5f, h, 100*h);
408 g_matrices.MatrixMode(MM_MODELVIEW);
411 matx = g_matrices.GetMatrix(MM_MODELVIEW);
412 memcpy(m_view, matx, 16 * sizeof(GLfloat));
413 matx = g_matrices.GetMatrix(MM_PROJECTION);
414 memcpy(m_projection, matx, 16 * sizeof(GLfloat));
416 g_graphicsContext.EndPaint();
419 void CRenderSystemGLES::Project(float &x, float &y, float &z)
421 GLfloat coordX, coordY, coordZ;
422 if (g_matrices.Project(x, y, z, m_view, m_projection, m_viewPort, &coordX, &coordY, &coordZ))
425 y = (float)(m_viewPort[1] + m_viewPort[3] - coordY);
430 bool CRenderSystemGLES::TestRender()
432 static float theta = 0.0;
434 //RESOLUTION_INFO resInfo = CDisplaySettings::Get().GetCurrentResolutionInfo();
435 //glViewport(0, 0, resInfo.iWidth, resInfo.iHeight);
437 g_matrices.PushMatrix();
438 g_matrices.Rotatef( theta, 0.0f, 0.0f, 1.0f );
440 EnableGUIShader(SM_DEFAULT);
442 GLfloat col[4] = {1.0f, 0.0f, 0.0f, 1.0f};
444 GLint posLoc = GUIShaderGetPos();
445 GLint colLoc = GUIShaderGetCol();
447 glVertexAttribPointer(posLoc, 2, GL_FLOAT, 0, 0, ver);
448 glVertexAttribPointer(colLoc, 4, GL_FLOAT, 0, 0, col);
450 glEnableVertexAttribArray(posLoc);
451 glEnableVertexAttribArray(colLoc);
453 // Setup vertex position values
461 glDrawArrays(GL_TRIANGLES, 0, 3);
463 glDisableVertexAttribArray(posLoc);
464 glDisableVertexAttribArray(colLoc);
468 g_matrices.PopMatrix();
475 void CRenderSystemGLES::ApplyHardwareTransform(const TransformMatrix &finalMatrix)
477 if (!m_bRenderCreated)
480 g_matrices.MatrixMode(MM_MODELVIEW);
481 g_matrices.PushMatrix();
482 GLfloat matrix[4][4];
484 for(int i = 0; i < 3; i++)
485 for(int j = 0; j < 4; j++)
486 matrix[j][i] = finalMatrix.m[i][j];
493 g_matrices.MultMatrixf(&matrix[0][0]);
496 void CRenderSystemGLES::RestoreHardwareTransform()
498 if (!m_bRenderCreated)
501 g_matrices.MatrixMode(MM_MODELVIEW);
502 g_matrices.PopMatrix();
505 void CRenderSystemGLES::CalculateMaxTexturesize()
507 // GLES cannot do PROXY textures to determine maximum size,
508 CLog::Log(LOGINFO, "GLES: Maximum texture width: %u", m_maxTextureSize);
511 void CRenderSystemGLES::GetViewPort(CRect& viewPort)
513 if (!m_bRenderCreated)
516 viewPort.x1 = m_viewPort[0];
517 viewPort.y1 = m_height - m_viewPort[1] - m_viewPort[3];
518 viewPort.x2 = m_viewPort[0] + m_viewPort[2];
519 viewPort.y2 = viewPort.y1 + m_viewPort[3];
522 // FIXME make me const so that I can accept temporary objects
523 void CRenderSystemGLES::SetViewPort(CRect& viewPort)
525 if (!m_bRenderCreated)
528 glScissor((GLint) viewPort.x1, (GLint) (m_height - viewPort.y1 - viewPort.Height()), (GLsizei) viewPort.Width(), (GLsizei) viewPort.Height());
529 glViewport((GLint) viewPort.x1, (GLint) (m_height - viewPort.y1 - viewPort.Height()), (GLsizei) viewPort.Width(), (GLsizei) viewPort.Height());
530 m_viewPort[0] = viewPort.x1;
531 m_viewPort[1] = m_height - viewPort.y1 - viewPort.Height();
532 m_viewPort[2] = viewPort.Width();
533 m_viewPort[3] = viewPort.Height();
536 void CRenderSystemGLES::SetScissors(const CRect &rect)
538 if (!m_bRenderCreated)
540 GLint x1 = MathUtils::round_int(rect.x1);
541 GLint y1 = MathUtils::round_int(rect.y1);
542 GLint x2 = MathUtils::round_int(rect.x2);
543 GLint y2 = MathUtils::round_int(rect.y2);
544 glScissor(x1, m_height - y2, x2-x1, y2-y1);
547 void CRenderSystemGLES::ResetScissors()
549 SetScissors(CRect(0, 0, (float)m_width, (float)m_height));
552 void CRenderSystemGLES::InitialiseGUIShader()
556 m_pGUIshader = new CGUIShader*[SM_ESHADERCOUNT];
557 for (int i = 0; i < SM_ESHADERCOUNT; i++)
559 if (i == SM_TEXTURE_RGBA_OES)
561 if (!g_Windowing.IsExtSupported("GL_OES_EGL_image_external"))
563 m_pGUIshader[i] = NULL;
568 m_pGUIshader[i] = new CGUIShader( ShaderNames[i] );
570 if (!m_pGUIshader[i]->CompileAndLink())
572 m_pGUIshader[i]->Free();
573 delete m_pGUIshader[i];
574 m_pGUIshader[i] = NULL;
575 CLog::Log(LOGERROR, "GUI Shader [%s] - Initialise failed", ShaderNames[i]);
579 CLog::Log(LOGDEBUG, "GUI Shader [%s]- Initialise successful : %p", ShaderNames[i], m_pGUIshader[i]);
585 CLog::Log(LOGDEBUG, "GUI Shader - Tried to Initialise again. Was this intentional?");
589 void CRenderSystemGLES::EnableGUIShader(ESHADERMETHOD method)
592 if (m_pGUIshader[m_method])
594 m_pGUIshader[m_method]->Enable();
598 CLog::Log(LOGERROR, "Invalid GUI Shader selected - [%s]", ShaderNames[(int)method]);
602 void CRenderSystemGLES::DisableGUIShader()
604 if (m_pGUIshader[m_method])
606 m_pGUIshader[m_method]->Disable();
608 m_method = SM_DEFAULT;
611 GLint CRenderSystemGLES::GUIShaderGetPos()
613 if (m_pGUIshader[m_method])
614 return m_pGUIshader[m_method]->GetPosLoc();
619 GLint CRenderSystemGLES::GUIShaderGetCol()
621 if (m_pGUIshader[m_method])
622 return m_pGUIshader[m_method]->GetColLoc();
627 GLint CRenderSystemGLES::GUIShaderGetCoord0()
629 if (m_pGUIshader[m_method])
630 return m_pGUIshader[m_method]->GetCord0Loc();
635 GLint CRenderSystemGLES::GUIShaderGetCoord1()
637 if (m_pGUIshader[m_method])
638 return m_pGUIshader[m_method]->GetCord1Loc();
643 GLint CRenderSystemGLES::GUIShaderGetUniCol()
645 if (m_pGUIshader[m_method])
646 return m_pGUIshader[m_method]->GetUniColLoc();
651 GLint CRenderSystemGLES::GUIShaderGetCoord0Matrix()
653 if (m_pGUIshader[m_method])
654 return m_pGUIshader[m_method]->GetCoord0MatrixLoc();