MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
mythrender_opengl.cpp
Go to the documentation of this file.
1 #include <QLibrary>
2 #include <algorithm>
3 using namespace std;
4 
5 #include "mythlogging.h"
6 #include "mythrender_opengl.h"
7 #include "mythxdisplay.h"
8 
9 #define LOC QString("OpenGL: ")
10 
11 #include "mythrender_opengl2.h"
12 #ifdef USING_OPENGLES
13 #include "mythrender_opengl2es.h"
14 #else
15 #include "mythrender_opengl1.h"
16 #endif
17 
18 #ifdef USING_X11
19 #include "util-nvctrl.h"
20 #endif
21 
22 static const GLuint kTextureOffset = 8 * sizeof(GLfloat);
23 
24 static inline int __glCheck__(const QString &loc, const char* fileName, int n)
25 {
26  int error = glGetError();
27  if (error)
28  {
29  LOG(VB_GENERAL, LOG_ERR, QString("%1: %2 @ %3, %4")
30  .arg(loc).arg(error).arg(fileName).arg(n));
31  }
32  return error;
33 }
34 
35 #define MAX_VERTEX_CACHE 500
36 #define glCheck() __glCheck__(LOC, __FILE__, __LINE__)
37 
38 OpenGLLocker::OpenGLLocker(MythRenderOpenGL *render) : m_render(render)
39 {
40  if (m_render)
42 }
43 
45 {
46  if (m_render)
48 }
49 
51  QPaintDevice* device)
52 {
53  QGLFormat format;
54  format.setDepth(false);
55 
56  bool setswapinterval = false;
57  int synctovblank = -1;
58 
59 #ifdef USING_X11
60  synctovblank = CheckNVOpenGLSyncToVBlank();
61 #endif
62 
63  if (synctovblank < 0)
64  {
65  LOG(VB_GENERAL, LOG_WARNING, LOC + "Could not determine whether Sync "
66  "to VBlank is enabled.");
67  }
68  else if (synctovblank == 0)
69  {
70  // currently only Linux NVidia is supported and there is no way of
71  // forcing sync to vblank after the app has started. util-nvctrl will
72  // warn the user and offer advice on settings.
73  }
74  else
75  {
76  LOG(VB_GENERAL, LOG_INFO, LOC + "Sync to VBlank is enabled (good!)");
77  }
78 
79 #if defined(Q_OS_MAC)
80  LOG(VB_GENERAL, LOG_INFO, LOC + "Forcing swap interval for OS X.");
81  setswapinterval = true;
82 #endif
83 
84  if (setswapinterval)
85  format.setSwapInterval(1);
86 
87 #ifdef USING_OPENGLES
88  if (device)
89  return new MythRenderOpenGL2ES(format, device);
90  return new MythRenderOpenGL2ES(format);
91 #else
92  if (painter.contains("opengl2"))
93  {
94  if (device)
95  return new MythRenderOpenGL2(format, device);
96  return new MythRenderOpenGL2(format);
97  }
98  if (device)
99  return new MythRenderOpenGL1(format, device);
100  return new MythRenderOpenGL1(format);
101 
102 #endif
103 }
104 
105 MythRenderOpenGL::MythRenderOpenGL(const QGLFormat& format, QPaintDevice* device,
107  : QGLContext(format, device), MythRender(type)
108 {
109 }
110 
112  : QGLContext(format), MythRender(type)
113 {
114 }
115 
117 {
118  delete m_lock;
119 }
120 
122 {
123  OpenGLLocker locker(this);
124  InitProcs();
125  Init2DState();
126  InitFeatures();
127 
128  LOG(VB_GENERAL, LOG_INFO, LOC + "Initialised MythRenderOpenGL");
129 }
130 
132 {
133  bool recommended = true;
134  OpenGLLocker locker(this);
135  QString renderer = (const char*) glGetString(GL_RENDERER);
136  if (!(this->format().directRendering()))
137  {
138  LOG(VB_GENERAL, LOG_WARNING, LOC +
139  "OpenGL is using software rendering.");
140  recommended = false;
141  }
142  else if (renderer.contains("Software Rasterizer", Qt::CaseInsensitive))
143  {
144  LOG(VB_GENERAL, LOG_WARNING, LOC +
145  "OpenGL is using software rasterizer.");
146  recommended = false;
147  }
148  else if (renderer.contains("softpipe", Qt::CaseInsensitive))
149  {
150  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL seems to be using software "
151  "fallback. Please check your OpenGL driver installation, "
152  "configuration, and device permissions.");
153  recommended = false;
154  }
155  return recommended;
156 }
157 
159 {
160  m_lock->lock();
161  if (this != MythRenderOpenGL::currentContext())
163  m_lock_level++;
164 }
165 
167 {
168  m_lock_level--;
169  if (m_lock_level == 0)
171  if (m_lock_level < 0)
172  LOG(VB_GENERAL, LOG_ERR, LOC + "Mis-matched calls to makeCurrent()");
173  m_lock->unlock();
174 }
175 
177 {
178 #if !defined(Q_OS_WIN)
179  while (m_lock_level > 0)
180  doneCurrent();
181 #endif
182 }
183 
184 void MythRenderOpenGL::MoveResizeWindow(const QRect &rect)
185 {
186  QWidget *parent = (QWidget*)this->device();
187  if (parent)
188  parent->setGeometry(rect);
189 }
190 
191 void MythRenderOpenGL::SetViewPort(const QRect &rect, bool viewportonly)
192 {
193  if (rect == m_viewport)
194  return;
195  makeCurrent();
196  m_viewport = rect;
197  glViewport(m_viewport.left(), m_viewport.top(),
198  m_viewport.width(), m_viewport.height());
199  if (!viewportonly)
200  SetMatrixView();
201  doneCurrent();
202 }
203 
204 void MythRenderOpenGL::Flush(bool use_fence)
205 {
206  makeCurrent();
207 
208  if ((m_exts_used & kGLAppleFence) &&
209  (m_fence && use_fence))
210  {
213  }
214  else if ((m_exts_used & kGLNVFence) &&
215  (m_fence && use_fence))
216  {
217  m_glSetFenceNV(m_fence, GL_ALL_COMPLETED_NV);
219  }
220  else
221  {
222  glFlush();
223  }
224 
225  doneCurrent();
226 }
227 
228 void MythRenderOpenGL::SetBlend(bool enable)
229 {
230  makeCurrent();
231  if (enable && !m_blend)
232  glEnable(GL_BLEND);
233  else if (!enable && m_blend)
234  glDisable(GL_BLEND);
235  m_blend = enable;
236  doneCurrent();
237 }
238 
239 void MythRenderOpenGL::SetBackground(int r, int g, int b, int a)
240 {
241  uint32_t tmp = (r << 24) + (g << 16) + (b << 8) + a;
242  if (tmp == m_background)
243  return;
244 
245  m_background = tmp;
246  makeCurrent();
247  glClearColor(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
248  doneCurrent();
249 }
250 
252 {
253  makeCurrent();
255  {
257  if (m_fence)
258  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Using GL_APPLE_fence");
259  }
260  else if (m_exts_used & kGLNVFence)
261  {
263  if (m_fence)
264  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Using GL_NV_fence");
265  }
266  doneCurrent();
267 }
268 
269 void* MythRenderOpenGL::GetTextureBuffer(uint tex, bool create_buffer)
270 {
271  if (!m_textures.contains(tex))
272  return NULL;
273 
274  makeCurrent(); // associated doneCurrent() in UpdateTexture
275 
276  EnableTextures(tex);
277  glBindTexture(m_textures[tex].m_type, tex);
278 
279  if (!create_buffer)
280  return NULL;
281 
282  if (m_textures[tex].m_pbo)
283  {
284  m_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_textures[tex].m_pbo);
285  m_glBufferData(GL_PIXEL_UNPACK_BUFFER,
286  m_textures[tex].m_data_size, NULL, GL_STREAM_DRAW);
287  return m_glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
288  }
289 
290  if (m_textures[tex].m_data)
291  return m_textures[tex].m_data;
292 
293  unsigned char *scratch = new unsigned char[m_textures[tex].m_data_size];
294  if (scratch)
295  {
296  memset(scratch, 0, m_textures[tex].m_data_size);
297  m_textures[tex].m_data = scratch;
298  }
299  return scratch;
300 }
301 
303 {
304  // N.B. GetTextureBuffer must be called first
305  if (!m_textures.contains(tex))
306  return;
307 
308  QSize size = m_textures[tex].m_act_size;
309 
310  if (m_textures[tex].m_pbo)
311  {
312  m_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
313  glTexSubImage2D(m_textures[tex].m_type, 0, 0, 0, size.width(),
314  size.height(), m_textures[tex].m_data_fmt,
315  m_textures[tex].m_data_type, 0);
316  m_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
317  }
318  else
319  {
320  glTexSubImage2D(m_textures[tex].m_type, 0, 0, 0, size.width(),
321  size.height(), m_textures[tex].m_data_fmt,
322  m_textures[tex].m_data_type, buf);
323  }
324 
325  doneCurrent();
326 }
327 
329 {
330  static bool rects = true;
331  static bool check = true;
332  if (check)
333  {
334  check = false;
335  rects = !getenv("OPENGL_NORECT");
336  if (!rects)
337  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling NPOT textures.");
338  }
339 
340  int ret = GL_TEXTURE_2D;
341 
342  if (m_extensions.contains("GL_NV_texture_rectangle") && rects)
343  ret = GL_TEXTURE_RECTANGLE_NV;
344  else if (m_extensions.contains("GL_ARB_texture_rectangle") && rects)
345  ret = GL_TEXTURE_RECTANGLE_ARB;
346  else if (m_extensions.contains("GL_EXT_texture_rectangle") && rects)
347  ret = GL_TEXTURE_RECTANGLE_EXT;
348 
349  rect = (ret != GL_TEXTURE_2D);
350  return ret;
351 }
352 
354 {
355  if (type == GL_TEXTURE_RECTANGLE_NV || type == GL_TEXTURE_RECTANGLE_ARB ||
356  type == GL_TEXTURE_RECTANGLE_EXT)
357  return true;
358  return false;
359 }
360 
361 uint MythRenderOpenGL::CreateTexture(QSize act_size, bool use_pbo,
362  uint type, uint data_type,
363  uint data_fmt, uint internal_fmt,
364  uint filter, uint wrap)
365 {
366  if (!type)
367  type = m_default_texture_type;
368 
369  QSize tot_size = GetTextureSize(type, act_size);
370 
371  makeCurrent();
372 
373  EnableTextures(0, type);
374 
375  GLuint tex;
376  glGenTextures(1, &tex);
377  glBindTexture(type, tex);
378 
379  if (tex)
380  {
382  texture.m_type = type;
383  texture.m_data_type = data_type;
384  texture.m_data_fmt = data_fmt;
385  texture.m_internal_fmt = internal_fmt;
386  texture.m_size = tot_size;
387  texture.m_act_size = act_size;
388  texture.m_data_size = GetBufferSize(act_size, data_fmt, data_type);
389  m_textures.insert(tex, texture);
390 
391  if (ClearTexture(tex) && m_textures[tex].m_data_size)
392  {
393  SetTextureFilters(tex, filter, wrap);
394  if (use_pbo)
395  m_textures[tex].m_pbo = CreatePBO(tex);
396  if (m_exts_used & kGLExtVBO)
397  m_textures[tex].m_vbo = CreateVBO();
398  }
399  else
400  {
401  DeleteTexture(tex);
402  tex = 0;
403  }
404  }
405 
406  Flush(true);
407  doneCurrent();
408 
409  return tex;
410 }
411 
413 {
414  if (IsRectTexture(type))
415  return size;
416 
417  int w = 64;
418  int h = 64;
419 
420  while (w < size.width())
421  {
422  w *= 2;
423  }
424 
425  while (h < size.height())
426  {
427  h *= 2;
428  }
429 
430  return QSize(w, h);
431 }
432 
434 {
435  if (!m_textures.contains(tex))
436  return QSize();
437  return m_textures[tex].m_size;
438 }
439 
441 {
442  if (!m_textures.contains(tex))
443  return 0;
444  return m_textures[tex].m_data_size;
445 }
446 
448 {
449  if (!m_textures.contains(tex))
450  return;
451 
452  bool mipmaps = (m_exts_used & kGLMipMaps) &&
454  if (filt == GL_LINEAR_MIPMAP_LINEAR && !mipmaps)
455  filt = GL_LINEAR;
456 
457  makeCurrent();
458  EnableTextures(tex);
459  m_textures[tex].m_filter = filt;
460  m_textures[tex].m_wrap = wrap;
461  uint type = m_textures[tex].m_type;
462  glBindTexture(type, tex);
463  uint mag_filt = filt;
464  if (filt == GL_LINEAR_MIPMAP_LINEAR)
465  {
466  mag_filt = GL_LINEAR;
467  glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
468  glTexParameteri(type, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
469  }
470  glTexParameteri(type, GL_TEXTURE_MIN_FILTER, filt);
471  glTexParameteri(type, GL_TEXTURE_MAG_FILTER, mag_filt);
472  glTexParameteri(type, GL_TEXTURE_WRAP_S, wrap);
473  if (type != GL_TEXTURE_1D)
474  glTexParameteri(type, GL_TEXTURE_WRAP_T, wrap);
475  doneCurrent();
476 }
477 
479 {
480  if (!(m_exts_used & kGLMultiTex))
481  return;
482 
483  makeCurrent();
484  if (m_active_tex != active_tex)
485  {
486  m_glActiveTexture(active_tex);
487  m_active_tex = active_tex;
488  }
489  doneCurrent();
490 }
491 
493 {
494  float w0 = (((-1 * x + 3) * x - 3) * x + 1) / 6;
495  float w1 = ((( 3 * x - 6) * x + 0) * x + 4) / 6;
496  float w2 = (((-3 * x + 3) * x + 3) * x + 1) / 6;
497  float w3 = ((( 1 * x + 0) * x + 0) * x + 0) / 6;
498  *dst++ = 1 + x - w1 / (w0 + w1);
499  *dst++ = 1 - x + w3 / (w2 + w3);
500  *dst++ = w0 + w1;
501  *dst++ = 0;
502 }
503 
505 {
506  if (tex && !m_textures.contains(tex))
507  return;
508 
509  makeCurrent();
510  int type = tex ? m_textures[tex].m_type : tex_type;
511  if (type != m_active_tex_type)
512  {
513  if (m_active_tex_type)
514  glDisable(m_active_tex_type);
515  glEnable(type);
517  }
518  doneCurrent();
519 }
520 
522 {
523  if (!m_active_tex_type)
524  return;
525  makeCurrent();
526  glDisable(m_active_tex_type);
527  m_active_tex_type = 0;
528  doneCurrent();
529 }
530 
532 {
533  if (!m_textures.contains(tex))
534  return;
535 
536  makeCurrent();
537 
538  GLuint gltex = tex;
539  glDeleteTextures(1, &gltex);
540  if (m_textures[tex].m_data)
541  delete m_textures[tex].m_data;
542  if (m_textures[tex].m_pbo)
543  m_glDeleteBuffers(1, &(m_textures[tex].m_pbo));
544  if (m_textures[tex].m_vbo)
545  m_glDeleteBuffers(1, &(m_textures[tex].m_vbo));
546  m_textures.remove(tex);
547 
548  Flush(true);
549  doneCurrent();
550 }
551 
553 {
554  if (!(m_exts_used & kGLExtFBufObj))
555  return false;
556 
557  if (!m_textures.contains(tex))
558  return false;
559 
560  QSize size = m_textures[tex].m_size;
561  GLuint glfb;
562 
563  makeCurrent();
564  glCheck();
565 
566  EnableTextures(tex);
567  QRect tmp_viewport = m_viewport;
568  glViewport(0, 0, size.width(), size.height());
569  m_glGenFramebuffers(1, &glfb);
570  m_glBindFramebuffer(GL_FRAMEBUFFER, glfb);
571  glBindTexture(m_textures[tex].m_type, tex);
572  glTexImage2D(m_textures[tex].m_type, 0, m_textures[tex].m_internal_fmt,
573  (GLint) size.width(), (GLint) size.height(), 0,
574  m_textures[tex].m_data_fmt, m_textures[tex].m_data_type, NULL);
575  m_glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
576  m_textures[tex].m_type, tex, 0);
577 
578  GLenum status;
579  status = m_glCheckFramebufferStatus(GL_FRAMEBUFFER);
580  m_glBindFramebuffer(GL_FRAMEBUFFER, 0);
581  glViewport(tmp_viewport.left(), tmp_viewport.top(),
582  tmp_viewport.width(), tmp_viewport.height());
583 
584  bool success = false;
585  switch (status)
586  {
587  case GL_FRAMEBUFFER_COMPLETE:
588  LOG(VB_PLAYBACK, LOG_INFO, LOC +
589  QString("Created frame buffer object (%1x%2).")
590  .arg(size.width()).arg(size.height()));
591  success = true;
592  break;
593  case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
594  LOG(VB_PLAYBACK, LOG_INFO, LOC +
595  "Frame buffer incomplete_ATTACHMENT");
596  break;
597  case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
598  LOG(VB_PLAYBACK, LOG_INFO, LOC +
599  "Frame buffer incomplete_MISSING_ATTACHMENT");
600  break;
601  case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT:
602  LOG(VB_PLAYBACK, LOG_INFO, LOC +
603  "Frame buffer incomplete_DUPLICATE_ATTACHMENT");
604  break;
605  case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
606  LOG(VB_PLAYBACK, LOG_INFO, LOC +
607  "Frame buffer incomplete_DIMENSIONS");
608  break;
609  case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
610  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Frame buffer incomplete_FORMATS");
611  break;
612  case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
613  LOG(VB_PLAYBACK, LOG_INFO, LOC +
614  "Frame buffer incomplete_DRAW_BUFFER");
615  break;
616  case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
617  LOG(VB_PLAYBACK, LOG_INFO, LOC +
618  "Frame buffer incomplete_READ_BUFFER");
619  break;
620  case GL_FRAMEBUFFER_UNSUPPORTED:
621  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Frame buffer unsupported.");
622  break;
623  default:
624  LOG(VB_PLAYBACK, LOG_INFO, LOC +
625  QString("Unknown frame buffer error %1.").arg(status));
626  }
627 
628  if (success)
629  m_framebuffers.push_back(glfb);
630  else
631  m_glDeleteFramebuffers(1, &glfb);
632 
633  Flush(true);
634  glCheck();
635  doneCurrent();
636  fb = glfb;
637  return success;
638 }
639 
641 {
642  if (!m_framebuffers.contains(fb))
643  return;
644 
645  makeCurrent();
646  QVector<GLuint>::iterator it;
647  for (it = m_framebuffers.begin(); it != m_framebuffers.end(); ++it)
648  {
649  if (*it == fb)
650  {
651  m_glDeleteFramebuffers(1, &(*it));
652  m_framebuffers.erase(it);
653  break;
654  }
655  }
656 
657  Flush(true);
658  doneCurrent();
659 }
660 
662 {
663  if (fb && !m_framebuffers.contains(fb))
664  return;
665 
666  if (fb == (uint)m_active_fb)
667  return;
668 
669  makeCurrent();
670  m_glBindFramebuffer(GL_FRAMEBUFFER, fb);
671  doneCurrent();
672  m_active_fb = fb;
673 }
674 
676 {
677  makeCurrent();
678  glClear(GL_COLOR_BUFFER_BIT);
679  doneCurrent();
680 }
681 
682 void MythRenderOpenGL::DrawBitmap(uint tex, uint target, const QRect *src,
683  const QRect *dst, uint prog, int alpha,
684  int red, int green, int blue)
685 {
686  if (!tex || !m_textures.contains(tex))
687  return;
688 
689  if (target && !m_framebuffers.contains(target))
690  target = 0;
691 
692  makeCurrent();
693  BindFramebuffer(target);
694  DrawBitmapPriv(tex, src, dst, prog, alpha, red, green, blue);
695  doneCurrent();
696 }
697 
698 void MythRenderOpenGL::DrawBitmap(uint *textures, uint texture_count,
699  uint target, const QRectF *src,
700  const QRectF *dst, uint prog)
701 {
702  if (!textures || !texture_count)
703  return;
704 
705  if (target && !m_framebuffers.contains(target))
706  target = 0;
707 
708  makeCurrent();
709  BindFramebuffer(target);
710  DrawBitmapPriv(textures, texture_count, src, dst, prog);
711  doneCurrent();
712 }
713 
714 void MythRenderOpenGL::DrawRect(const QRect &area, const QBrush &fillBrush,
715  const QPen &linePen, int alpha)
716 {
717  makeCurrent();
718  BindFramebuffer(0);
719  DrawRectPriv(area, fillBrush, linePen, alpha);
720  doneCurrent();
721 }
722 
723 void MythRenderOpenGL::DrawRoundRect(const QRect &area, int cornerRadius,
724  const QBrush &fillBrush,
725  const QPen &linePen, int alpha)
726 {
727  makeCurrent();
728  BindFramebuffer(0);
729  DrawRoundRectPriv(area, cornerRadius, fillBrush, linePen, alpha);
730  doneCurrent();
731 }
732 
734 {
735  SetBlend(false);
736  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
737  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
738  glDisable(GL_DEPTH_TEST);
739  glDepthMask(GL_FALSE);
740  glDisable(GL_CULL_FACE);
741  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
742  glClear(GL_COLOR_BUFFER_BIT);
743  Flush(true);
744 }
745 
747 {
748  m_extensions = (reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
749 
750  m_glTexImage1D = (MYTH_GLTEXIMAGE1DPROC)
751  GetProcAddress("glTexImage1D");
752  m_glActiveTexture = (MYTH_GLACTIVETEXTUREPROC)
753  GetProcAddress("glActiveTexture");
755  GetProcAddress("glMapBuffer");
756  m_glBindBuffer = (MYTH_GLBINDBUFFERPROC)
757  GetProcAddress("glBindBuffer");
758  m_glGenBuffers = (MYTH_GLGENBUFFERSPROC)
759  GetProcAddress("glGenBuffers");
760  m_glBufferData = (MYTH_GLBUFFERDATAPROC)
761  GetProcAddress("glBufferData");
762  m_glUnmapBuffer = (MYTH_GLUNMAPBUFFERPROC)
763  GetProcAddress("glUnmapBuffer");
764  m_glDeleteBuffers = (MYTH_GLDELETEBUFFERSPROC)
765  GetProcAddress("glDeleteBuffers");
766  m_glGenFramebuffers = (MYTH_GLGENFRAMEBUFFERSPROC)
767  GetProcAddress("glGenFramebuffers");
768  m_glBindFramebuffer = (MYTH_GLBINDFRAMEBUFFERPROC)
769  GetProcAddress("glBindFramebuffer");
770  m_glFramebufferTexture2D = (MYTH_GLFRAMEBUFFERTEXTURE2DPROC)
771  GetProcAddress("glFramebufferTexture2D");
772  m_glCheckFramebufferStatus = (MYTH_GLCHECKFRAMEBUFFERSTATUSPROC)
773  GetProcAddress("glCheckFramebufferStatus");
774  m_glDeleteFramebuffers = (MYTH_GLDELETEFRAMEBUFFERSPROC)
775  GetProcAddress("glDeleteFramebuffers");
776  m_glGenFencesNV = (MYTH_GLGENFENCESNVPROC)
777  GetProcAddress("glGenFencesNV");
778  m_glDeleteFencesNV = (MYTH_GLDELETEFENCESNVPROC)
779  GetProcAddress("glDeleteFencesNV");
780  m_glSetFenceNV = (MYTH_GLSETFENCENVPROC)
781  GetProcAddress("glSetFenceNV");
782  m_glFinishFenceNV = (MYTH_GLFINISHFENCENVPROC)
783  GetProcAddress("glFinishFenceNV");
785  GetProcAddress("glGenFencesAPPLE");
787  GetProcAddress("glDeleteFencesAPPLE");
789  GetProcAddress("glSetFenceAPPLE");
791  GetProcAddress("glFinishFenceAPPLE");
792 }
793 
794 void* MythRenderOpenGL::GetProcAddress(const QString &proc) const
795 {
796  // TODO FIXME - this should really return a void(*) not void*
797  static const QString exts[4] = { "", "ARB", "EXT", "OES" };
798  void *result;
799  for (int i = 0; i < 4; i++)
800  {
801  result = reinterpret_cast<void*>(
802  QLibrary::resolve("libGLESv2", (proc + exts[i]).toLatin1().data()));
803  if (result)
804  break;
805  result = reinterpret_cast<void*>(getProcAddress(proc + exts[i]));
806  if (result)
807  break;
808  }
809  if (result == NULL)
810  LOG(VB_GENERAL, LOG_DEBUG, LOC +
811  QString("Extension not found: %1").arg(proc));
812 
813  return result;
814 }
815 
817 {
818  static bool multitexture = true;
819  static bool vertexarrays = true;
820  static bool framebuffers = true;
821  static bool pixelbuffers = true;
822  static bool vertexbuffers = true;
823  static bool fences = true;
824  static bool ycbcrtextures = true;
825  static bool mipmapping = true;
826  static bool check = true;
827 
828  if (check)
829  {
830  check = false;
831  multitexture = !getenv("OPENGL_NOMULTITEX");
832  vertexarrays = !getenv("OPENGL_NOVERTARRAY");
833  framebuffers = !getenv("OPENGL_NOFBO");
834  pixelbuffers = !getenv("OPENGL_NOPBO");
835  vertexbuffers = !getenv("OPENGL_NOVBO");
836  fences = !getenv("OPENGL_NOFENCE");
837  ycbcrtextures = !getenv("OPENGL_NOYCBCR");
838  mipmapping = !getenv("OPENGL_NOMIPMAP");
839  if (!multitexture)
840  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling multi-texturing.");
841  if (!vertexarrays)
842  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling Vertex Arrays.");
843  if (!framebuffers)
844  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling Framebuffer Objects.");
845  if (!pixelbuffers)
846  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling Pixel Buffer Objects.");
847  if (!vertexbuffers)
848  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling Vertex Buffer Objects.");
849  if (!fences)
850  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling fences.");
851  if (!ycbcrtextures)
852  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling YCbCr textures.");
853  if (!mipmapping)
854  LOG(VB_GENERAL, LOG_INFO, LOC + "Disabling mipmapping.");
855  }
856 
857  GLint maxtexsz = 0;
858  GLint maxunits = 0;
859  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsz);
860  glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxunits);
861  m_max_units = maxunits;
862  m_max_tex_size = (maxtexsz) ? maxtexsz : 512;
863 
864  m_extensions = (const char*) glGetString(GL_EXTENSIONS);
865  bool rects;
867  if (rects)
869 
870  if (m_extensions.contains("GL_ARB_multitexture") &&
871  m_glActiveTexture && multitexture)
872  {
874  if (m_max_units < 3)
875  {
876  LOG(VB_GENERAL, LOG_ERR, LOC +
877  "Insufficient texture units for advanced OpenGL features.");
878  }
879  }
880  else
881  {
882  LOG(VB_GENERAL, LOG_ERR, LOC + "Multi-texturing not supported. Certain "
883  "OpenGL features will not work");
884  }
885 
886  if (m_extensions.contains("GL_EXT_vertex_array") && vertexarrays)
887  {
889  }
890  else
891  {
892  LOG(VB_GENERAL, LOG_ERR, LOC +
893  "GL_EXT_vertex_array extension not supported. This may not work");
894  }
895 
896  if (m_extensions.contains("GL_EXT_framebuffer_object") &&
901 
902  bool buffer_procs = m_glMapBuffer && m_glBindBuffer &&
905 
906  if(m_extensions.contains("GL_ARB_pixel_buffer_object")
907  && buffer_procs && pixelbuffers)
909 
910  if (m_extensions.contains("GL_ARB_vertex_buffer_object")
911  && buffer_procs && vertexbuffers)
913 
914  if(m_extensions.contains("GL_NV_fence") &&
918 
919  if(m_extensions.contains("GL_APPLE_fence") &&
923 
924  if (m_extensions.contains("GL_MESA_ycbcr_texture") && ycbcrtextures)
926 
927  if (m_extensions.contains("GL_APPLE_ycbcr_422") && ycbcrtextures)
929 
930  if (m_extensions.contains("GL_SGIS_generate_mipmap") && mipmapping)
932 
933  static bool debugged = false;
934  if (!debugged)
935  {
936  debugged = true;
937  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL vendor : %1")
938  .arg((const char*) glGetString(GL_VENDOR)));
939  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL renderer: %1")
940  .arg((const char*) glGetString(GL_RENDERER)));
941  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL version : %1")
942  .arg((const char*) glGetString(GL_VERSION)));
943  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Max texture size: %1 x %2")
944  .arg(m_max_tex_size).arg(m_max_tex_size));
945  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Max texture units: %1")
946  .arg(m_max_units));
947  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Direct rendering: %1")
948  .arg((this->format().directRendering()) ? "Yes" : "No"));
949  }
950 
952 
954  {
955  LOG(VB_GENERAL, LOG_INFO, LOC + "PixelBufferObject support available");
956  }
957 
958  return true;
959 }
960 
962 {
963  m_fence = 0;
964 
965  m_lock = new QMutex(QMutex::Recursive);
966  m_lock_level = 0;
967 
968  m_extensions = QString();
971  m_max_tex_size = 0;
972  m_max_units = 0;
973  m_default_texture_type = GL_TEXTURE_2D;
974 
975  m_viewport = QRect();
976  m_active_tex = 0;
977  m_active_tex_type = 0;
978  m_active_fb = 0;
979  m_blend = false;
980  m_background = 0x00000000;
981 }
982 
984 {
985  m_extensions = QString();
986 
987  m_glTexImage1D = NULL;
988  m_glActiveTexture = NULL;
989  m_glMapBuffer = NULL;
990  m_glBindBuffer = NULL;
991  m_glGenBuffers = NULL;
992  m_glBufferData = NULL;
993  m_glUnmapBuffer = NULL;
994  m_glDeleteBuffers = NULL;
995  m_glGenFramebuffers = NULL;
996  m_glBindFramebuffer = NULL;
999  m_glDeleteFramebuffers = NULL;
1000  m_glGenFencesNV = NULL;
1001  m_glDeleteFencesNV = NULL;
1002  m_glSetFenceNV = NULL;
1003  m_glFinishFenceNV = NULL;
1004  m_glGenFencesAPPLE = NULL;
1005  m_glDeleteFencesAPPLE = NULL;
1006  m_glSetFenceAPPLE = NULL;
1007  m_glFinishFenceAPPLE = NULL;
1008 }
1009 
1011 {
1012  if (!(m_exts_used & kGLExtPBufObj))
1013  return 0;
1014 
1015  if (!m_textures.contains(tex))
1016  return 0;
1017 
1018  m_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
1019  glTexImage2D(m_textures[tex].m_type, 0, m_textures[tex].m_internal_fmt,
1020  m_textures[tex].m_size.width(),
1021  m_textures[tex].m_size.height(), 0,
1022  m_textures[tex].m_data_fmt, m_textures[tex].m_data_type, NULL);
1023 
1024  GLuint tmp_pbo;
1025  m_glGenBuffers(1, &tmp_pbo);
1026  m_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
1027 
1028  Flush(true);
1029  return tmp_pbo;
1030 }
1031 
1033 {
1034  if (!(m_exts_used & kGLExtVBO))
1035  return 0;
1036 
1037  GLuint tmp_vbo;
1038  m_glGenBuffers(1, &tmp_vbo);
1039  return tmp_vbo;
1040 }
1041 
1043 {
1044  LOG(VB_GENERAL, LOG_INFO, LOC + "Deleting OpenGL Resources");
1045  DeleteTextures();
1047  Flush(true);
1048 
1049  if (m_fence)
1050  {
1053  else if(m_exts_supported & kGLNVFence)
1055  m_fence = 0;
1056  }
1057 
1058  Flush(false);
1059 
1060  ExpireVertices();
1061  ExpireVBOS();
1062 
1063  if (m_cachedVertices.size())
1064  {
1065  LOG(VB_GENERAL, LOG_ERR, LOC + QString(" %1 unexpired vertices")
1066  .arg(m_cachedVertices.size()));
1067  }
1068 
1069  if (m_cachedVBOS.size())
1070  {
1071  LOG(VB_GENERAL, LOG_ERR, LOC + QString(" %1 unexpired VBOs")
1072  .arg(m_cachedVertices.size()));
1073  }
1074 }
1075 
1077 {
1078  QHash<GLuint, MythGLTexture>::iterator it;
1079  for (it = m_textures.begin(); it !=m_textures.end(); ++it)
1080  {
1081  glDeleteTextures(1, &(it.key()));
1082  if (it.value().m_data)
1083  delete it.value().m_data;
1084  if (it.value().m_pbo)
1085  m_glDeleteBuffers(1, &(it.value().m_pbo));
1086  }
1087  m_textures.clear();
1088  Flush(true);
1089 }
1090 
1092 {
1093  QVector<GLuint>::iterator it;
1094  for (it = m_framebuffers.begin(); it != m_framebuffers.end(); ++it)
1095  m_glDeleteFramebuffers(1, &(*(it)));
1096  m_framebuffers.clear();
1097  Flush(true);
1098 }
1099 
1101  const QRect *dst)
1102 {
1103  if (!m_textures.contains(tex))
1104  return false;
1105 
1106  GLfloat *data = m_textures[tex].m_vertex_data;
1107  QSize size = m_textures[tex].m_size;
1108 
1109  int width = min(src->width(), size.width());
1110  int height = min(src->height(), size.height());
1111 
1112  data[0 + TEX_OFFSET] = src->left();
1113  data[1 + TEX_OFFSET] = src->top() + height;
1114 
1115  data[6 + TEX_OFFSET] = src->left() + width;
1116  data[7 + TEX_OFFSET] = src->top();
1117 
1118  if (!IsRectTexture(m_textures[tex].m_type))
1119  {
1120  data[0 + TEX_OFFSET] /= (float)size.width();
1121  data[6 + TEX_OFFSET] /= (float)size.width();
1122  data[1 + TEX_OFFSET] /= (float)size.height();
1123  data[7 + TEX_OFFSET] /= (float)size.height();
1124  }
1125 
1126  data[2 + TEX_OFFSET] = data[0 + TEX_OFFSET];
1127  data[3 + TEX_OFFSET] = data[7 + TEX_OFFSET];
1128  data[4 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1129  data[5 + TEX_OFFSET] = data[1 + TEX_OFFSET];
1130 
1131  data[2] = data[0] = dst->left();
1132  data[5] = data[1] = dst->top();
1133  data[4] = data[6] = dst->left() + min(width, dst->width());
1134  data[3] = data[7] = dst->top() + min(height, dst->height());
1135 
1136  return true;
1137 }
1138 
1140  const QRectF *dst)
1141 {
1142  if (!m_textures.contains(tex))
1143  return false;
1144 
1145  GLfloat *data = m_textures[tex].m_vertex_data;
1146 
1147  data[0 + TEX_OFFSET] = src->left();
1148  data[1 + TEX_OFFSET] = src->top() + src->height();
1149 
1150  data[6 + TEX_OFFSET] = src->left() + src->width();
1151  data[7 + TEX_OFFSET] = src->top();
1152 
1153  if (!IsRectTexture(m_textures[tex].m_type))
1154  {
1155  data[0 + TEX_OFFSET] /= (float)m_textures[tex].m_size.width();
1156  data[6 + TEX_OFFSET] /= (float)m_textures[tex].m_size.width();
1157  data[1 + TEX_OFFSET] /= (float)m_textures[tex].m_size.height();
1158  data[7 + TEX_OFFSET] /= (float)m_textures[tex].m_size.height();
1159  }
1160 
1161  data[2 + TEX_OFFSET] = data[0 + TEX_OFFSET];
1162  data[3 + TEX_OFFSET] = data[7 + TEX_OFFSET];
1163  data[4 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1164  data[5 + TEX_OFFSET] = data[1 + TEX_OFFSET];
1165 
1166  data[2] = data[0] = dst->left();
1167  data[5] = data[1] = dst->top();
1168  data[4] = data[6] = dst->left() + dst->width();
1169  data[3] = data[7] = dst->top() + dst->height();
1170 
1171  return true;
1172 }
1173 
1174 GLfloat* MythRenderOpenGL::GetCachedVertices(GLuint type, const QRect &area)
1175 {
1176  uint64_t ref = ((uint64_t)area.left() & 0xfff) +
1177  (((uint64_t)area.top() & 0xfff) << 12) +
1178  (((uint64_t)area.width() & 0xfff) << 24) +
1179  (((uint64_t)area.height() & 0xfff) << 36) +
1180  (((uint64_t)type & 0xfff) << 48);
1181 
1182  if (m_cachedVertices.contains(ref))
1183  {
1184  m_vertexExpiry.removeOne(ref);
1185  m_vertexExpiry.append(ref);
1186  return m_cachedVertices[ref];
1187  }
1188 
1189  GLfloat *vertices = new GLfloat[8];
1190 
1191  vertices[2] = vertices[0] = area.left();
1192  vertices[5] = vertices[1] = area.top();
1193  vertices[4] = vertices[6] = area.left() + area.width();
1194  vertices[3] = vertices[7] = area.top() + area.height();
1195 
1196  if (type == GL_LINE_LOOP)
1197  {
1198  vertices[7] = vertices[1];
1199  vertices[5] = vertices[3];
1200  }
1201 
1202  m_cachedVertices.insert(ref, vertices);
1203  m_vertexExpiry.append(ref);
1204  ExpireVertices(MAX_VERTEX_CACHE);
1205 
1206  return vertices;
1207 }
1208 
1210 {
1211  while ((uint)m_vertexExpiry.size() > max)
1212  {
1213  uint64_t ref = m_vertexExpiry.first();
1214  m_vertexExpiry.removeFirst();
1215  GLfloat *vertices = NULL;
1216  if (m_cachedVertices.contains(ref))
1217  vertices = m_cachedVertices.value(ref);
1218  m_cachedVertices.remove(ref);
1219  delete [] vertices;
1220  }
1221 }
1222 
1223 void MythRenderOpenGL::GetCachedVBO(GLuint type, const QRect &area)
1224 {
1225  uint64_t ref = ((uint64_t)area.left() & 0xfff) +
1226  (((uint64_t)area.top() & 0xfff) << 12) +
1227  (((uint64_t)area.width() & 0xfff) << 24) +
1228  (((uint64_t)area.height() & 0xfff) << 36) +
1229  (((uint64_t)type & 0xfff) << 48);
1230 
1231  if (m_cachedVBOS.contains(ref))
1232  {
1233  m_vboExpiry.removeOne(ref);
1234  m_vboExpiry.append(ref);
1235  }
1236  else
1237  {
1238  GLfloat *vertices = GetCachedVertices(type, area);
1239  GLuint vbo = CreateVBO();
1240  m_cachedVBOS.insert(ref, vbo);
1241  m_vboExpiry.append(ref);
1242 
1243  m_glBindBuffer(GL_ARRAY_BUFFER, vbo);
1244  m_glBufferData(GL_ARRAY_BUFFER, kTextureOffset, NULL, GL_STREAM_DRAW);
1245  void* target = m_glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
1246  if (target)
1247  memcpy(target, vertices, kTextureOffset);
1248  m_glUnmapBuffer(GL_ARRAY_BUFFER);
1249 
1250  ExpireVBOS(MAX_VERTEX_CACHE);
1251  return;
1252  }
1253 
1254  m_glBindBuffer(GL_ARRAY_BUFFER, m_cachedVBOS.value(ref));
1255 }
1256 
1258 {
1259  while ((uint)m_vboExpiry.size() > max)
1260  {
1261  uint64_t ref = m_vboExpiry.first();
1262  m_vboExpiry.removeFirst();
1263  if (m_cachedVBOS.contains(ref))
1264  {
1265  GLuint vbo = m_cachedVBOS.value(ref);
1266  m_glDeleteBuffers(1, &vbo);
1267  m_cachedVBOS.remove(ref);
1268  }
1269  }
1270 }
1271 
1273 {
1274  if (!m_textures.contains(tex))
1275  return false;
1276 
1277  QSize size = m_textures[tex].m_size;
1278  uint tmp_size = GetBufferSize(size, m_textures[tex].m_data_fmt,
1279  m_textures[tex].m_data_type);
1280 
1281  if (!tmp_size)
1282  return false;
1283 
1284  unsigned char *scratch = new unsigned char[tmp_size];
1285 
1286  if (!scratch)
1287  return false;
1288 
1289  memset(scratch, 0, tmp_size);
1290 
1291  if ((m_textures[tex].m_type == GL_TEXTURE_1D) && m_glTexImage1D)
1292  {
1294  m_textures[tex].m_internal_fmt,
1295  size.width(), 0, m_textures[tex].m_data_fmt,
1296  m_textures[tex].m_data_type, scratch);
1297  }
1298  else
1299  {
1300  glTexImage2D(m_textures[tex].m_type, 0, m_textures[tex].m_internal_fmt,
1301  size.width(), size.height(), 0, m_textures[tex].m_data_fmt,
1302  m_textures[tex].m_data_type, scratch);
1303  }
1304  delete [] scratch;
1305 
1306  return true;
1307 }
1308 
1310 {
1311  uint bytes;
1312  uint bpp;
1313 
1314  if (fmt == GL_BGRA || fmt ==GL_RGBA)
1315  {
1316  bpp = 4;
1317  }
1318  else if (fmt == GL_YCBCR_MESA || fmt == GL_YCBCR_422_APPLE ||
1319  fmt == MYTHTV_UYVY)
1320  {
1321  bpp = 2;
1322  }
1323  else
1324  {
1325  bpp =0;
1326  }
1327 
1328  switch (type)
1329  {
1330  case GL_UNSIGNED_BYTE:
1331  bytes = sizeof(GLubyte);
1332  break;
1333  case GL_UNSIGNED_SHORT_8_8_MESA:
1334  bytes = sizeof(GLushort);
1335  break;
1336  case GL_FLOAT:
1337  bytes = sizeof(GLfloat);
1338  break;
1339  default:
1340  bytes = 0;
1341  }
1342 
1343  if (!bpp || !bytes || size.width() < 1 || size.height() < 1)
1344  return 0;
1345 
1346  return size.width() * size.height() * bpp * bytes;
1347 }