[release] version bump to 13.0 beta1
[vuplus_xbmc] / xbmc / rendering / gles / RenderSystemGLES.cpp
1 /*
2  *      Copyright (C) 2005-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
21
22 #include "system.h"
23
24 #if HAS_GLES == 2
25
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"
36
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"
47     };
48
49 CRenderSystemGLES::CRenderSystemGLES()
50  : CRenderSystemBase()
51  , m_pGUIshader(0)
52  , m_method(SM_DEFAULT)
53 {
54   m_enumRenderingSystem = RENDERING_SYSTEM_OPENGLES;
55 }
56
57 CRenderSystemGLES::~CRenderSystemGLES()
58 {
59 }
60
61 bool CRenderSystemGLES::InitRenderSystem()
62 {
63   GLint maxTextureSize;
64
65   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
66
67   m_maxTextureSize = maxTextureSize;
68   m_bVSync = false;
69   m_iVSyncMode = 0;
70   m_iSwapStamp = 0;
71   m_iSwapTime = 0;
72   m_iSwapRate = 0;
73   m_bVsyncInit = false;
74   m_renderCaps = 0;
75   // Get the GLES version number
76   m_RenderVersionMajor = 0;
77   m_RenderVersionMinor = 0;
78
79   const char* ver = (const char*)glGetString(GL_VERSION);
80   if (ver != 0)
81   {
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;
86   }
87   
88   // Get our driver vendor and renderer
89   m_RenderVendor = (const char*) glGetString(GL_VENDOR);
90   m_RenderRenderer = (const char*) glGetString(GL_RENDERER);
91
92   m_RenderExtensions  = " ";
93   m_RenderExtensions += (const char*) glGetString(GL_EXTENSIONS);
94   m_RenderExtensions += " ";
95
96   LogGraphicsInfo();
97   
98   if (IsExtSupported("GL_TEXTURE_NPOT"))
99   {
100     m_renderCaps |= RENDER_CAPS_NPOT;
101   }
102
103   if (IsExtSupported("GL_EXT_texture_format_BGRA8888"))
104   {
105     m_renderCaps |= RENDER_CAPS_BGRA;
106   }
107
108   if (IsExtSupported("GL_IMG_texture_format_BGRA8888"))
109   {
110     m_renderCaps |= RENDER_CAPS_BGRA;
111   }
112
113   if (IsExtSupported("GL_APPLE_texture_format_BGRA8888"))
114   {
115     m_renderCaps |= RENDER_CAPS_BGRA_APPLE;
116   }
117
118
119
120   m_bRenderCreated = true;
121   
122   InitialiseGUIShader();
123
124   return true;
125 }
126
127 bool CRenderSystemGLES::ResetRenderSystem(int width, int height, bool fullScreen, float refreshRate)
128 {
129   m_width = width;
130   m_height = height;
131   
132   glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
133   CalculateMaxTexturesize();
134
135   CRect rect( 0, 0, width, height );
136   SetViewPort( rect );
137
138   glEnable(GL_SCISSOR_TEST); 
139
140   g_matrices.MatrixMode(MM_PROJECTION);
141   g_matrices.LoadIdentity();
142
143   g_matrices.Ortho(0.0f, width-1, height-1, 0.0f, -1.0f, 1.0f);
144
145   g_matrices.MatrixMode(MM_MODELVIEW);
146   g_matrices.LoadIdentity();
147   
148   glBlendFunc(GL_SRC_ALPHA, GL_ONE);
149   glEnable(GL_BLEND);          // Turn Blending On
150   glDisable(GL_DEPTH_TEST);  
151     
152   return true;
153 }
154
155 bool CRenderSystemGLES::DestroyRenderSystem()
156 {
157   CLog::Log(LOGDEBUG, "GUI Shader - Destroying Shader : %p", m_pGUIshader);
158
159   if (m_pGUIshader)
160   {
161     for (int i = 0; i < SM_ESHADERCOUNT; i++)
162     {
163       if (m_pGUIshader[i])
164       {
165         m_pGUIshader[i]->Free();
166         delete m_pGUIshader[i];
167         m_pGUIshader[i] = NULL;
168       }
169     }
170     delete[] m_pGUIshader;
171     m_pGUIshader = NULL;
172   }
173
174   ResetScissors();
175   CDirtyRegionList dirtyRegions;
176   CDirtyRegion dirtyWindow(g_graphicsContext.GetViewWindow());
177   dirtyRegions.push_back(dirtyWindow);
178
179   ClearBuffers(0);
180   glFinish();
181   PresentRenderImpl(dirtyRegions);
182
183   m_bRenderCreated = false;
184
185   return true;
186 }
187
188 bool CRenderSystemGLES::BeginRender()
189 {
190   if (!m_bRenderCreated)
191     return false;
192
193   return true;
194 }
195
196 bool CRenderSystemGLES::EndRender()
197 {
198   if (!m_bRenderCreated)
199     return false;
200
201   return true;
202 }
203
204 bool CRenderSystemGLES::ClearBuffers(color_t color)
205 {
206   if (!m_bRenderCreated)
207     return false;
208
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;
213
214   glClearColor(r, g, b, a);
215
216   GLbitfield flags = GL_COLOR_BUFFER_BIT;
217   glClear(flags);
218
219   return true;
220 }
221
222 bool CRenderSystemGLES::IsExtSupported(const char* extension)
223 {
224   if (strcmp( extension, "GL_EXT_framebuffer_object" ) == 0)
225   {
226     // GLES has FBO as a core element, not an extension!
227     return true;
228   }
229   else if (strcmp( extension, "GL_TEXTURE_NPOT" ) == 0)
230   {
231     // GLES supports non-power-of-two textures as standard.
232         return true;
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
237          */
238   }
239   else
240   {
241     CStdString name;
242     name  = " ";
243     name += extension;
244     name += " ";
245
246     bool supported = m_RenderExtensions.find(name) != std::string::npos;
247     CLog::Log(LOGDEBUG, "GLES: Extension Support Test - %s %s", extension, supported ? "YES" : "NO");
248     return supported;
249   }
250 }
251
252 static int64_t abs64(int64_t a)
253 {
254   if(a < 0)
255     return -a;
256   return a;
257 }
258
259 bool CRenderSystemGLES::PresentRender(const CDirtyRegionList &dirty)
260 {
261   if (!m_bRenderCreated)
262     return false;
263
264   if (m_iVSyncMode != 0 && m_iSwapRate != 0) 
265   {
266     int64_t curr, diff, freq;
267     curr = CurrentHostCounter();
268     freq = CurrentHostFrequency();
269
270     if(m_iSwapStamp == 0)
271       m_iSwapStamp = curr;
272
273     /* calculate our next swap timestamp */
274     diff = curr - m_iSwapStamp;
275     diff = m_iSwapRate - diff % m_iSwapRate;
276     m_iSwapStamp = curr + diff;
277
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;
282     if (diff > 0)
283       Sleep((DWORD)diff);
284   }
285   
286   bool result = PresentRenderImpl(dirty);
287   
288   if (m_iVSyncMode && m_iSwapRate != 0)
289   {
290     int64_t curr, diff;
291     curr = CurrentHostCounter();
292
293     diff = curr - m_iSwapStamp;
294     m_iSwapStamp = curr;
295
296     if (abs64(diff - m_iSwapRate) < abs64(diff))
297       CLog::Log(LOGDEBUG, "%s - missed requested swap",__FUNCTION__);
298   }
299   
300   return result;
301 }
302
303 void CRenderSystemGLES::SetVSync(bool enable)
304 {
305   if (m_bVSync==enable && m_bVsyncInit == true)
306     return;
307
308   if (!m_bRenderCreated)
309     return;
310   
311   if (enable)
312     CLog::Log(LOGINFO, "GLES: Enabling VSYNC");
313   else
314     CLog::Log(LOGINFO, "GLES: Disabling VSYNC");
315
316   m_iVSyncMode   = 0;
317   m_iVSyncErrors = 0;
318   m_iSwapRate    = 0;
319   m_bVSync       = enable;
320   m_bVsyncInit   = true;
321
322   SetVSyncImpl(enable);
323   
324   if (!enable)
325     return;
326
327   if (g_advancedSettings.m_ForcedSwapTime != 0.0)
328   {
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)
332     {
333       CLog::Log(LOGWARNING, "Unable to determine a valid horizontal refresh rate, vsync workaround disabled %.2g", rate);
334       m_iSwapRate = 0;
335     }
336     else
337     {
338       int64_t freq;
339       freq = CurrentHostFrequency();
340       m_iSwapRate   = (int64_t)((double)freq / rate);
341       m_iSwapTime   = (int64_t)(0.001 * g_advancedSettings.m_ForcedSwapTime * freq);
342       m_iSwapStamp  = 0;
343       CLog::Log(LOGINFO, "GLES: Using artificial vsync sleep with rate %f", rate);
344       if(!m_iVSyncMode)
345         m_iVSyncMode = 1;
346     }
347   }
348     
349   if (!m_iVSyncMode)
350     CLog::Log(LOGERROR, "GLES: Vertical Blank Syncing unsupported");
351   else
352     CLog::Log(LOGINFO, "GLES: Selected vsync mode %d", m_iVSyncMode);
353 }
354
355 void CRenderSystemGLES::CaptureStateBlock()
356 {
357   if (!m_bRenderCreated)
358     return;
359
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);
370 }
371
372 void CRenderSystemGLES::ApplyStateBlock()
373 {
374   if (!m_bRenderCreated)
375     return;
376
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);
384   glEnable(GL_BLEND);
385   glEnable(GL_SCISSOR_TEST);  
386   glClear(GL_DEPTH_BUFFER_BIT);
387 }
388
389 void CRenderSystemGLES::SetCameraPosition(const CPoint &camera, int screenWidth, int screenHeight)
390
391   if (!m_bRenderCreated)
392     return;
393   
394   g_graphicsContext.BeginPaint();
395   
396   CPoint offset = camera - CPoint(screenWidth*0.5f, screenHeight*0.5f);
397   
398   float w = (float)m_viewPort[2]*0.5f;
399   float h = (float)m_viewPort[3]*0.5f;
400
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);
409
410   GLfloat* matx;
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));
415
416   g_graphicsContext.EndPaint();
417 }
418
419 void CRenderSystemGLES::Project(float &x, float &y, float &z)
420 {
421   GLfloat coordX, coordY, coordZ;
422   if (g_matrices.Project(x, y, z, m_view, m_projection, m_viewPort, &coordX, &coordY, &coordZ))
423   {
424     x = coordX;
425     y = (float)(m_viewPort[1] + m_viewPort[3] - coordY);
426     z = 0;
427   }
428 }
429
430 bool CRenderSystemGLES::TestRender()
431 {
432   static float theta = 0.0;
433
434   //RESOLUTION_INFO resInfo = CDisplaySettings::Get().GetCurrentResolutionInfo();
435   //glViewport(0, 0, resInfo.iWidth, resInfo.iHeight);
436
437   g_matrices.PushMatrix();
438   g_matrices.Rotatef( theta, 0.0f, 0.0f, 1.0f );
439
440   EnableGUIShader(SM_DEFAULT);
441
442   GLfloat col[4] = {1.0f, 0.0f, 0.0f, 1.0f};
443   GLfloat ver[3][2];
444   GLint   posLoc = GUIShaderGetPos();
445   GLint   colLoc = GUIShaderGetCol();
446
447   glVertexAttribPointer(posLoc,  2, GL_FLOAT, 0, 0, ver);
448   glVertexAttribPointer(colLoc,  4, GL_FLOAT, 0, 0, col);
449
450   glEnableVertexAttribArray(posLoc);
451   glEnableVertexAttribArray(colLoc);
452
453   // Setup vertex position values
454   ver[0][0] =  0.0f;
455   ver[0][1] =  1.0f;
456   ver[1][0] =  0.87f;
457   ver[1][1] = -0.5f;
458   ver[2][0] = -0.87f;
459   ver[2][1] = -0.5f;
460
461   glDrawArrays(GL_TRIANGLES, 0, 3);
462
463   glDisableVertexAttribArray(posLoc);
464   glDisableVertexAttribArray(colLoc);
465
466   DisableGUIShader();
467
468   g_matrices.PopMatrix();
469
470   theta += 1.0f;
471
472   return true;
473 }
474
475 void CRenderSystemGLES::ApplyHardwareTransform(const TransformMatrix &finalMatrix)
476
477   if (!m_bRenderCreated)
478     return;
479
480   g_matrices.MatrixMode(MM_MODELVIEW);
481   g_matrices.PushMatrix();
482   GLfloat matrix[4][4];
483
484   for(int i = 0; i < 3; i++)
485     for(int j = 0; j < 4; j++)
486       matrix[j][i] = finalMatrix.m[i][j];
487
488   matrix[0][3] = 0.0f;
489   matrix[1][3] = 0.0f;
490   matrix[2][3] = 0.0f;
491   matrix[3][3] = 1.0f;
492
493   g_matrices.MultMatrixf(&matrix[0][0]);
494 }
495
496 void CRenderSystemGLES::RestoreHardwareTransform()
497 {
498   if (!m_bRenderCreated)
499     return;
500
501   g_matrices.MatrixMode(MM_MODELVIEW);
502   g_matrices.PopMatrix();
503 }
504
505 void CRenderSystemGLES::CalculateMaxTexturesize()
506 {
507   // GLES cannot do PROXY textures to determine maximum size,
508   CLog::Log(LOGINFO, "GLES: Maximum texture width: %u", m_maxTextureSize);
509 }
510
511 void CRenderSystemGLES::GetViewPort(CRect& viewPort)
512 {
513   if (!m_bRenderCreated)
514     return;
515
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];
520 }
521
522 // FIXME make me const so that I can accept temporary objects
523 void CRenderSystemGLES::SetViewPort(CRect& viewPort)
524 {
525   if (!m_bRenderCreated)
526     return;
527
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();
534 }
535
536 void CRenderSystemGLES::SetScissors(const CRect &rect)
537 {
538   if (!m_bRenderCreated)
539     return;
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);
545 }
546
547 void CRenderSystemGLES::ResetScissors()
548 {
549   SetScissors(CRect(0, 0, (float)m_width, (float)m_height));
550 }
551
552 void CRenderSystemGLES::InitialiseGUIShader()
553 {
554   if (!m_pGUIshader)
555   {
556     m_pGUIshader = new CGUIShader*[SM_ESHADERCOUNT];
557     for (int i = 0; i < SM_ESHADERCOUNT; i++)
558     {
559       if (i == SM_TEXTURE_RGBA_OES)
560       {
561         if (!g_Windowing.IsExtSupported("GL_OES_EGL_image_external"))
562         {
563           m_pGUIshader[i] = NULL;
564           continue;
565         }
566       }
567
568       m_pGUIshader[i] = new CGUIShader( ShaderNames[i] );
569
570       if (!m_pGUIshader[i]->CompileAndLink())
571       {
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]);
576       }
577       else
578       {
579         CLog::Log(LOGDEBUG, "GUI Shader [%s]- Initialise successful : %p", ShaderNames[i], m_pGUIshader[i]);
580       }
581     }
582   }
583   else
584   {
585     CLog::Log(LOGDEBUG, "GUI Shader - Tried to Initialise again. Was this intentional?");
586   }
587 }
588
589 void CRenderSystemGLES::EnableGUIShader(ESHADERMETHOD method)
590 {
591   m_method = method;
592   if (m_pGUIshader[m_method])
593   {
594     m_pGUIshader[m_method]->Enable();
595   }
596   else
597   {
598     CLog::Log(LOGERROR, "Invalid GUI Shader selected - [%s]", ShaderNames[(int)method]);
599   }
600 }
601
602 void CRenderSystemGLES::DisableGUIShader()
603 {
604   if (m_pGUIshader[m_method])
605   {
606     m_pGUIshader[m_method]->Disable();
607   }
608   m_method = SM_DEFAULT;
609 }
610
611 GLint CRenderSystemGLES::GUIShaderGetPos()
612 {
613   if (m_pGUIshader[m_method])
614     return m_pGUIshader[m_method]->GetPosLoc();
615
616   return -1;
617 }
618
619 GLint CRenderSystemGLES::GUIShaderGetCol()
620 {
621   if (m_pGUIshader[m_method])
622     return m_pGUIshader[m_method]->GetColLoc();
623
624   return -1;
625 }
626
627 GLint CRenderSystemGLES::GUIShaderGetCoord0()
628 {
629   if (m_pGUIshader[m_method])
630     return m_pGUIshader[m_method]->GetCord0Loc();
631
632   return -1;
633 }
634
635 GLint CRenderSystemGLES::GUIShaderGetCoord1()
636 {
637   if (m_pGUIshader[m_method])
638     return m_pGUIshader[m_method]->GetCord1Loc();
639
640   return -1;
641 }
642
643 GLint CRenderSystemGLES::GUIShaderGetUniCol()
644 {
645   if (m_pGUIshader[m_method])
646     return m_pGUIshader[m_method]->GetUniColLoc();
647
648   return -1;
649 }
650
651 GLint CRenderSystemGLES::GUIShaderGetCoord0Matrix()
652 {
653   if (m_pGUIshader[m_method])
654     return m_pGUIshader[m_method]->GetCoord0MatrixLoc();
655
656   return -1;
657 }
658
659 #endif