MythTV  master
mythrenderopengl.cpp
Go to the documentation of this file.
1 // Std
2 #include <algorithm>
3 #include <cmath>
4 using std::min;
5 
6 // Qt
7 #include <QLibrary>
8 #include <QPainter>
9 #include <QWindow>
10 #include <QWidget>
11 #include <QGuiApplication>
12 #include <QOpenGLExtraFunctions>
13 
14 // MythTV
15 #include "mythcorecontext.h"
16 #include "mythmainwindow.h"
17 #include "mythrenderopengl.h"
19 #include "mythlogging.h"
20 #include "mythuitype.h"
21 #include "mythxdisplay.h"
22 #define LOC QString("OpenGL: ")
23 
24 #ifdef Q_OS_ANDROID
25 #include <android/log.h>
26 #include <QWindow>
27 #endif
28 
29 #define VERTEX_INDEX 0
30 #define COLOR_INDEX 1
31 #define TEXTURE_INDEX 2
32 #define VERTEX_SIZE 2
33 #define TEXTURE_SIZE 2
34 
35 static const GLuint kVertexOffset = 0;
36 static const GLuint kTextureOffset = 8 * sizeof(GLfloat);
37 const GLuint MythRenderOpenGL::kVertexSize = 16 * sizeof(GLfloat);
38 
39 #define MAX_VERTEX_CACHE 500
40 
41 MythGLTexture::MythGLTexture(QOpenGLTexture *Texture)
42  : m_texture(Texture)
43 {
44 }
45 
47  : m_textureId(Texture)
48 {
49 }
50 
52  : m_render(Render)
53 {
54  if (m_render)
56 }
57 
59 {
60  if (m_render)
62 }
63 
65 {
66  // Don't try and create the window
67  if (!HasMythMainWindow())
68  return nullptr;
69 
71  if (!window)
72  return nullptr;
73 
74  auto* result = dynamic_cast<MythRenderOpenGL*>(window->GetRenderDevice());
75  if (result)
76  return result;
77  return nullptr;
78 }
79 
81 {
82  if (!Widget)
83  return nullptr;
84 
85  QString display = getenv("DISPLAY");
86  // Determine if we are running a remote X11 session
87  // DISPLAY=:x or DISPLAY=unix:x are local
88  // DISPLAY=hostname:x is remote
89  // DISPLAY=/xxx/xxx/.../org.macosforge.xquartz:x is local OS X
90  // x can be numbers n or n.n
91  // Anything else including DISPLAY not set is assumed local,
92  // in that case we are probably not running under X11
93  if (!display.isEmpty()
94  && !display.startsWith(":")
95  && !display.startsWith("unix:")
96  && !display.startsWith("/")
97  && display.contains(':'))
98  {
99  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL is disabled for Remote X Session");
100  return nullptr;
101  }
102 
103  // N.B the core profiles below are designed to target compute shader availability
104  bool opengles = !qgetenv("MYTHTV_OPENGL_ES").isEmpty();
105  bool core = !qgetenv("MYTHTV_OPENGL_CORE").isEmpty();
106  QSurfaceFormat format = QSurfaceFormat::defaultFormat();
107  if (core)
108  {
109  format.setProfile(QSurfaceFormat::CoreProfile);
110  format.setMajorVersion(4);
111  format.setMinorVersion(3);
112  }
113 
114  if (opengles)
115  {
116  if (core)
117  {
118  format.setProfile(QSurfaceFormat::CoreProfile);
119  format.setMajorVersion(3);
120  format.setMinorVersion(1);
121  }
122  format.setRenderableType(QSurfaceFormat::OpenGLES);
123  }
124 
125  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
126  format.setOption(QSurfaceFormat::DebugContext);
127 
128  return new MythRenderOpenGL(format, Widget);
129 }
130 
131 MythRenderOpenGL::MythRenderOpenGL(const QSurfaceFormat& Format, QWidget *Widget)
132  : QOpenGLContext(),
133  QOpenGLFunctions(),
134  MythEGL(this),
136  m_fullRange(gCoreContext->GetBoolSetting("GUIRGBLevels", true))
137 {
138  memset(m_defaultPrograms, 0, sizeof(m_defaultPrograms));
139  m_projection.fill(0);
140  m_parameters.fill(0);
141  m_transforms.push(QMatrix4x4());
142  setFormat(Format);
143  connect(this, &QOpenGLContext::aboutToBeDestroyed, this, &MythRenderOpenGL::contextToBeDestroyed);
144  SetWidget(Widget);
145 }
146 
148 {
149  LOG(VB_GENERAL, LOG_INFO, LOC + "MythRenderOpenGL closing");
150  if (!isValid())
151  return;
152  disconnect(this, &QOpenGLContext::aboutToBeDestroyed, this, &MythRenderOpenGL::contextToBeDestroyed);
153  if (m_ready)
155 }
156 
157 void MythRenderOpenGL::messageLogged(const QOpenGLDebugMessage &Message)
158 {
159  // filter unwanted messages
160  if (m_openGLDebuggerFilter & Message.type())
161  return;
162 
163  QString source("Unknown");
164  QString type("Unknown");
165 
166  switch (Message.source())
167  {
168  case QOpenGLDebugMessage::ApplicationSource: return; // filter out our own messages
169  case QOpenGLDebugMessage::APISource: source = "API"; break;
170  case QOpenGLDebugMessage::WindowSystemSource: source = "WinSys"; break;
171  case QOpenGLDebugMessage::ShaderCompilerSource: source = "ShaderComp"; break;
172  case QOpenGLDebugMessage::ThirdPartySource: source = "3rdParty"; break;
173  case QOpenGLDebugMessage::OtherSource: source = "Other"; break;
174  default: break;
175  }
176 
177  // N.B. each break is on a separate line to allow setting individual break points
178  // when using synchronous logging
179  switch (Message.type())
180  {
181  case QOpenGLDebugMessage::ErrorType:
182  type = "Error"; break;
183  case QOpenGLDebugMessage::DeprecatedBehaviorType:
184  type = "Deprecated"; break;
185  case QOpenGLDebugMessage::UndefinedBehaviorType:
186  type = "Undef behaviour"; break;
187  case QOpenGLDebugMessage::PortabilityType:
188  type = "Portability"; break;
189  case QOpenGLDebugMessage::PerformanceType:
190  type = "Performance"; break;
191  case QOpenGLDebugMessage::OtherType:
192  type = "Other"; break;
193  case QOpenGLDebugMessage::MarkerType:
194  type = "Marker"; break;
195  case QOpenGLDebugMessage::GroupPushType:
196  type = "GroupPush"; break;
197  case QOpenGLDebugMessage::GroupPopType:
198  type = "GroupPop"; break;
199  default: break;
200  }
201  LOG(VB_GPU, LOG_INFO, LOC + QString("Src: %1 Type: %2 Msg: %3").arg(source).arg(type).arg(Message.message()));
202 }
203 
204 void MythRenderOpenGL:: logDebugMarker(const QString &Message)
205 {
206  if (m_openglDebugger)
207  {
208  QOpenGLDebugMessage message = QOpenGLDebugMessage::createApplicationMessage(
209  Message, 0, QOpenGLDebugMessage::NotificationSeverity, QOpenGLDebugMessage::MarkerType);
210  m_openglDebugger->logMessage(message);
211  }
212 }
213 
214 // Can't be static because its connected to a signal and passed "this".
215 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
217 {
218  LOG(VB_GENERAL, LOG_WARNING, LOC + "Context about to be destroyed");
219 }
220 
222 {
223  if (!isValid())
224  {
225  LOG(VB_GENERAL, LOG_ERR, LOC + "MythRenderOpenGL is not a valid OpenGL rendering context");
226  return false;
227  }
228 
229  OpenGLLocker locker(this);
230  initializeOpenGLFunctions();
231  m_ready = true;
232  m_features = openGLFeatures();
233 
234  // don't enable this by default - it can generate a lot of detail
235  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
236  {
237  m_openglDebugger = new QOpenGLDebugLogger();
238  if (m_openglDebugger->initialize())
239  {
240  connect(m_openglDebugger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(messageLogged(QOpenGLDebugMessage)));
241  QOpenGLDebugLogger::LoggingMode mode = QOpenGLDebugLogger::AsynchronousLogging;
242 
243  // this will impact performance but can be very useful
244  if (!qgetenv("MYTHTV_OPENGL_SYNCHRONOUS").isEmpty())
245  mode = QOpenGLDebugLogger::SynchronousLogging;
246 
247  m_openglDebugger->startLogging(mode);
248  if (mode == QOpenGLDebugLogger::AsynchronousLogging)
249  LOG(VB_GENERAL, LOG_INFO, LOC + "GPU debug logging started (async)");
250  else
251  LOG(VB_GENERAL, LOG_INFO, LOC + "Started synchronous GPU debug logging (will hurt performance)");
252 
253  // filter messages. Some drivers can be extremely verbose for certain issues.
254  QStringList debug;
255  QString filter = qgetenv("MYTHTV_OPENGL_LOGFILTER");
256  if (filter.contains("other", Qt::CaseInsensitive))
257  {
258  m_openGLDebuggerFilter |= QOpenGLDebugMessage::OtherType;
259  debug << "Other";
260  }
261  if (filter.contains("error", Qt::CaseInsensitive))
262  {
263  m_openGLDebuggerFilter |= QOpenGLDebugMessage::ErrorType;
264  debug << "Error";
265  }
266  if (filter.contains("deprecated", Qt::CaseInsensitive))
267  {
268  m_openGLDebuggerFilter |= QOpenGLDebugMessage::DeprecatedBehaviorType;
269  debug << "Deprecated";
270  }
271  if (filter.contains("undefined", Qt::CaseInsensitive))
272  {
273  m_openGLDebuggerFilter |= QOpenGLDebugMessage::UndefinedBehaviorType;
274  debug << "Undefined";
275  }
276  if (filter.contains("portability", Qt::CaseInsensitive))
277  {
278  m_openGLDebuggerFilter |= QOpenGLDebugMessage::PortabilityType;
279  debug << "Portability";
280  }
281  if (filter.contains("performance", Qt::CaseInsensitive))
282  {
283  m_openGLDebuggerFilter |= QOpenGLDebugMessage::PerformanceType;
284  debug << "Performance";
285  }
286  if (filter.contains("grouppush", Qt::CaseInsensitive))
287  {
288  m_openGLDebuggerFilter |= QOpenGLDebugMessage::GroupPushType;
289  debug << "GroupPush";
290  }
291  if (filter.contains("grouppop", Qt::CaseInsensitive))
292  {
293  m_openGLDebuggerFilter |= QOpenGLDebugMessage::GroupPopType;
294  debug << "GroupPop";
295  }
296 
297  if (!debug.isEmpty())
298  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Filtering out GPU messages for: %1")
299  .arg(debug.join(", ")));
300  }
301  else
302  {
303  LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to initialise OpenGL logging");
304  delete m_openglDebugger;
305  m_openglDebugger = nullptr;
306  }
307  }
308 
309  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
310  logDebugMarker("RENDER_INIT_START");
311 
312  Init2DState();
313 
314  // basic features
315  GLint maxtexsz = 0;
316  GLint maxunits = 0;
317  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsz);
318  glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxunits);
319  m_maxTextureUnits = maxunits;
320  m_maxTextureSize = (maxtexsz) ? maxtexsz : 512;
321  QSurfaceFormat fmt = format();
322  m_colorDepth = qMin(fmt.redBufferSize(), qMin(fmt.greenBufferSize(), fmt.blueBufferSize()));
323 
324  // RGBA16 - available on ES via extension
325  m_extraFeatures |= isOpenGLES() ? hasExtension("GL_EXT_texture_norm16") ? kGLExtRGBA16 : kGLFeatNone : kGLExtRGBA16;
326 
327  // Pixel buffer objects
328  bool buffer_procs = reinterpret_cast<MYTH_GLMAPBUFFERPROC>(GetProcAddress("glMapBuffer")) &&
329  reinterpret_cast<MYTH_GLUNMAPBUFFERPROC>(GetProcAddress("glUnmapBuffer"));
330 
331  // Buffers are available by default (GL and GLES).
332  // Buffer mapping is available by extension
333  if ((isOpenGLES() && hasExtension("GL_OES_mapbuffer") && buffer_procs) ||
334  (hasExtension("GL_ARB_vertex_buffer_object") && buffer_procs))
336 
337  // Rectangular textures
338  if (!isOpenGLES() && (hasExtension("GL_NV_texture_rectangle") ||
339  hasExtension("GL_ARB_texture_rectangle") ||
340  hasExtension("GL_EXT_texture_rectangle")))
342 
343  // GL_RED etc texure formats. Not available on GLES2.0 or GL < 2
344  if ((isOpenGLES() && format().majorVersion() < 3) ||
345  (!isOpenGLES() && format().majorVersion() < 2))
347 
348  // GL_UNPACK_ROW_LENGTH - for uploading video textures
349  // Note: Should also be available on GL1.4 per specification
350  if (!isOpenGLES() || (isOpenGLES() && ((fmt.majorVersion() >= 3) || hasExtension("GL_EXT_unpack_subimage"))))
352 
353  // check for core profile
354  m_coreProfile = fmt.profile() == QSurfaceFormat::OpenGLContextProfile::CoreProfile;
355 
356  // if we have a core profile then we need a VAO bound - this is just a
357  // workaround for the time being
358  if (m_coreProfile)
359  {
360  QOpenGLExtraFunctions extra(nullptr);
361  extra.initializeOpenGLFunctions();
362  extra.glGenVertexArrays(1, &m_vao);
363  extra.glBindVertexArray(m_vao);
364  }
365 
366  // For (embedded) GPUs that use tile based rendering, it is faster to use
367  // glClear e.g. on the Pi3 it improves video frame rate significantly. Using
368  // glClear tells the GPU it doesn't have to retrieve the old framebuffer and will
369  // also clear existing draw calls.
370  // For now this just includes Broadcom VideoCoreIV.
371  // Other Tile Based Deferred Rendering GPUS - PowerVR5/6/7, Apple (PowerVR as well?)
372  // Other Tile Based Immediate Mode Rendering GPUS - ARM Mali, Qualcomm Adreno
373  static const QByteArray kTiled[3] = { "videocore", "vc4", "v3d" };
374  auto renderer = QByteArray(reinterpret_cast<const char*>(glGetString(GL_RENDERER))).toLower();
375  for (const auto & name : kTiled)
376  {
377  if (renderer.contains(name))
378  {
380  break;
381  }
382  }
383 
384  // Check for memory extensions
385  if (hasExtension("GL_NVX_gpu_memory_info"))
387 
388  DebugFeatures();
389 
391  if (!CreateDefaultShaders())
392  {
393  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create default shaders");
394  return false;
395  }
396 
397  LOG(VB_GENERAL, LOG_INFO, LOC + "Initialised MythRenderOpenGL");
398  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1 range output").arg(m_fullRange ? "full" : "limited"));
399  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
400  logDebugMarker("RENDER_INIT_END");
401  return true;
402 }
403 
404 #define GLYesNo(arg) ((arg) ? "Yes" : "No")
405 
407 {
408  static bool s_debugged = false;
409  if (s_debugged)
410  return;
411  s_debugged = true;
412  QSurfaceFormat fmt = format();
413  QString qtglversion = QString("OpenGL%1 %2.%3")
414  .arg(fmt.renderableType() == QSurfaceFormat::OpenGLES ? "ES" : "")
415  .arg(fmt.majorVersion()).arg(fmt.minorVersion());
416  QString qtglsurface = QString("RGBA: %1%2%3%4 Depth: %5 Stencil: %6")
417  .arg(fmt.redBufferSize()).arg(fmt.greenBufferSize())
418  .arg(fmt.greenBufferSize()).arg(fmt.alphaBufferSize())
419  .arg(fmt.depthBufferSize()).arg(fmt.stencilBufferSize());
420  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL vendor : %1").arg(reinterpret_cast<const char*>(glGetString(GL_VENDOR))));
421  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL renderer : %1").arg(reinterpret_cast<const char*>(glGetString(GL_RENDERER))));
422  LOG(VB_GENERAL, LOG_INFO, LOC + QString("OpenGL version : %1").arg(reinterpret_cast<const char*>(glGetString(GL_VERSION))));
423  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Qt platform : %1").arg(QGuiApplication::platformName()));
424 #ifdef USING_EGL
425  bool eglfuncs = IsEGL();
426  LOG(VB_GENERAL, LOG_INFO, LOC + QString("EGL display : %1").arg(GLYesNo(GetEGLDisplay() != nullptr)));
427  LOG(VB_GENERAL, LOG_INFO, LOC + QString("EGL images : %1").arg(GLYesNo(eglfuncs)));
428 #endif
429  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Qt OpenGL format : %1").arg(qtglversion));
430  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Qt OpenGL surface : %1").arg(qtglsurface));
431  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Max texture size : %1").arg(m_maxTextureSize));
432  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Max texture units : %1").arg(m_maxTextureUnits));
433  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Shaders : %1").arg(GLYesNo(m_features & Shaders)));
434  LOG(VB_GENERAL, LOG_INFO, LOC + QString("NPOT textures : %1").arg(GLYesNo(m_features & NPOTTextures)));
435  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Multitexturing : %1").arg(GLYesNo(m_features & Multitexture)));
436  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Rectangular textures : %1").arg(GLYesNo(m_extraFeatures & kGLExtRects)));
437  //LOG(VB_GENERAL, LOG_INFO, LOC + QString("RGBA16 textures : %1").arg(GLYesNo(m_extraFeatures & kGLExtRGBA16)));
438  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Buffer mapping : %1").arg(GLYesNo(m_extraFeatures & kGLBufferMap)));
439  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Framebuffer objects : %1").arg(GLYesNo(m_features & Framebuffers)));
440  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Unpack Subimage : %1").arg(GLYesNo(m_extraFeatures & kGLExtSubimage)));
441  LOG(VB_GENERAL, LOG_INFO, LOC + QString("GL_RED/GL_R8 : %1").arg(GLYesNo(!(m_extraFeatures & kGLLegacyTextures))));
442  //LOG(VB_GENERAL, LOG_INFO, LOC + QString("Compute shaders : %1").arg(GLYesNo(QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Compute))));
443 
444  // warnings
445  if (m_maxTextureUnits < 3)
446  LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning: Insufficient texture units for some features.");
447 }
448 
450 {
451  return m_colorDepth;
452 }
453 
455 {
456  return m_maxTextureSize;
457 }
458 
460 {
461  return m_maxTextureUnits;
462 }
463 
465 {
466  return m_extraFeaturesUsed;
467 }
468 
469 QOpenGLFunctions::OpenGLFeatures MythRenderOpenGL::GetFeatures(void) const
470 {
471  return m_features;
472 }
473 
475 {
476  if (!IsReady())
477  return false;
478 
479  bool recommended = true;
480  OpenGLLocker locker(this);
481  QString renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
482 
483  if (!(openGLFeatures() & Shaders))
484  {
485  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL has no shader support");
486  recommended = false;
487  }
488  else if (!(openGLFeatures() & Framebuffers))
489  {
490  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL has no framebuffer support");
491  recommended = false;
492  }
493  else if (renderer.contains("Software Rasterizer", Qt::CaseInsensitive))
494  {
495  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL is using software rasterizer.");
496  recommended = false;
497  }
498  else if (renderer.contains("softpipe", Qt::CaseInsensitive))
499  {
500  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenGL seems to be using software "
501  "fallback. Please check your OpenGL driver installation, "
502  "configuration, and device permissions.");
503  recommended = false;
504  }
505 
506  if (!recommended)
507  {
508  LOG(VB_GENERAL, LOG_INFO, LOC +
509  "OpenGL not recommended with this system's hardware/drivers.");
510  }
511 
512  return recommended;
513 }
514 
516 {
517  return isValid() && m_ready;
518 }
519 
521 {
522  QOpenGLContext::swapBuffers(m_window);
523 }
524 
525 void MythRenderOpenGL::SetWidget(QWidget *Widget)
526 {
527  if (!Widget)
528  {
529  LOG(VB_GENERAL, LOG_CRIT, LOC + "No widget!");
530  return;
531  }
532 
533  // We must have a window/surface.
534  m_window = Widget->windowHandle();
535  QWidget* native = Widget->nativeParentWidget();
536  if (!m_window && native)
537  m_window = native->windowHandle();
538 
539  if (!m_window)
540  {
541  LOG(VB_GENERAL, LOG_CRIT, LOC + "No window surface!");
542  return;
543  }
544 
545 #ifdef ANDROID
546  // Ensure surface type is always OpenGL
547  m_window->setSurfaceType(QWindow::OpenGLSurface);
548  if (native && native->windowHandle())
549  native->windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
550 #endif
551 
552  if (!create())
553  LOG(VB_GENERAL, LOG_CRIT, LOC + "Failed to create OpenGLContext!");
554  else
555  Widget->setAttribute(Qt::WA_PaintOnScreen);
556 }
557 
559 {
560  m_lock.lock();
561  if (!m_lockLevel++)
562  if (!QOpenGLContext::makeCurrent(m_window))
563  LOG(VB_GENERAL, LOG_ERR, LOC + "makeCurrent failed");
564 }
565 
567 {
568  // TODO add back QOpenGLContext::doneCurrent call
569  // once calls are better pipelined
570  m_lockLevel--;
571  if (m_lockLevel < 0)
572  LOG(VB_GENERAL, LOG_ERR, LOC + "Mis-matched calls to makeCurrent()");
573  m_lock.unlock();
574 }
575 
576 void MythRenderOpenGL::SetViewPort(const QRect &rect, bool viewportonly)
577 {
578  if (rect == m_viewport)
579  return;
580  makeCurrent();
581  m_viewport = rect;
582  glViewport(m_viewport.left(), m_viewport.top(),
583  m_viewport.width(), m_viewport.height());
584  if (!viewportonly)
585  SetMatrixView();
586  doneCurrent();
587 }
588 
590 {
591  if (!m_flushEnabled)
592  return;
593 
594  makeCurrent();
595  glFlush();
596  doneCurrent();
597 }
598 
599 void MythRenderOpenGL::SetBlend(bool enable)
600 {
601  makeCurrent();
602  if (enable && !m_blend)
603  glEnable(GL_BLEND);
604  else if (!enable && m_blend)
605  glDisable(GL_BLEND);
606  m_blend = enable;
607  doneCurrent();
608 }
609 
610 void MythRenderOpenGL::SetBackground(int r, int g, int b, int a)
611 {
612  int32_t tmp = (r << 24) + (g << 16) + (b << 8) + a;
613  if (tmp == m_background)
614  return;
615 
616  m_background = tmp;
617  makeCurrent();
618  glClearColor(r / 255.0F, g / 255.0F, b / 255.0F, a / 255.0F);
619  doneCurrent();
620 }
621 
623 {
624  if (!Image)
625  return nullptr;
626 
627  OpenGLLocker locker(this);
628  auto *texture = new QOpenGLTexture(*Image, QOpenGLTexture::DontGenerateMipMaps);
629  if (!texture->textureId())
630  {
631  LOG(VB_GENERAL, LOG_INFO, LOC + "Failed to create texure");
632  delete texture;
633  return nullptr;
634  }
635  texture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
636  texture->setWrapMode(QOpenGLTexture::ClampToEdge);
637  auto *result = new MythGLTexture(texture);
638  result->m_texture = texture;
639  result->m_vbo = CreateVBO(kVertexSize);
640  result->m_totalSize = GetTextureSize(Image->size(), result->m_target != QOpenGLTexture::TargetRectangle);
641  // N.B. Format and type per qopengltexure.cpp
642  result->m_pixelFormat = QOpenGLTexture::RGBA;
643  result->m_pixelType = QOpenGLTexture::UInt8;
644  result->m_bufferSize = GetBufferSize(result->m_totalSize, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
645  result->m_size = Image->size();
646  result->m_crop = true;
647  return result;
648 }
649 
650 QSize MythRenderOpenGL::GetTextureSize(const QSize &size, bool Normalised)
651 {
652  if ((m_features & NPOTTextures) || !Normalised)
653  return size;
654 
655  int w = 64;
656  int h = 64;
657  while (w < size.width())
658  w *= 2;
659  while (h < size.height())
660  h *= 2;
661  return {w, h};
662 }
663 
665 {
666  if (Texture)
667  return Texture->m_bufferSize;
668  return 0;
669 }
670 
671 void MythRenderOpenGL::SetTextureFilters(MythGLTexture *Texture, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap)
672 {
673  if (!Texture || (Texture && !(Texture->m_texture || Texture->m_textureId)))
674  return;
675 
676  makeCurrent();
677  if (Texture->m_texture)
678  {
679  Texture->m_texture->bind();
680  Texture->m_texture->setWrapMode(Wrap);
681  Texture->m_texture->setMinMagFilters(Filter, Filter);
682  }
683  else
684  {
685  glBindTexture(Texture->m_target, Texture->m_textureId);
686  glTexParameteri(Texture->m_target, GL_TEXTURE_MIN_FILTER, Filter);
687  glTexParameteri(Texture->m_target, GL_TEXTURE_MAG_FILTER, Filter);
688  glTexParameteri(Texture->m_target, GL_TEXTURE_WRAP_S, Wrap);
689  glTexParameteri(Texture->m_target, GL_TEXTURE_WRAP_T, Wrap);
690  }
691  doneCurrent();
692 }
693 
694 void MythRenderOpenGL::ActiveTexture(GLuint ActiveTex)
695 {
696  if (!(m_features & Multitexture))
697  return;
698 
699  makeCurrent();
700  if (m_activeTexture != ActiveTex)
701  {
702  glActiveTexture(ActiveTex);
703  m_activeTexture = ActiveTex;
704  }
705  doneCurrent();
706 }
707 
709 {
710  if (!Texture)
711  return;
712 
713  makeCurrent();
714  // N.B. Don't delete m_textureId - it is owned externally
715  delete Texture->m_texture;
716  delete [] Texture->m_data;
717  delete Texture->m_vbo;
718  delete Texture;
719  Flush();
720  doneCurrent();
721 }
722 
723 QOpenGLFramebufferObject* MythRenderOpenGL::CreateFramebuffer(QSize &Size, GLenum InternalFormat)
724 {
725  if (!(m_features & Framebuffers))
726  return nullptr;
727 
728  OpenGLLocker locker(this);
729  QOpenGLFramebufferObject *framebuffer = nullptr;
730  if (InternalFormat)
731  {
732  framebuffer = new QOpenGLFramebufferObject(Size, QOpenGLFramebufferObject::NoAttachment,
733  GL_TEXTURE_2D, InternalFormat);
734  }
735  else
736  {
737  framebuffer = new QOpenGLFramebufferObject(Size);
738  }
739  if (framebuffer->isValid())
740  {
741  if (framebuffer->isBound())
742  {
743  m_activeFramebuffer = framebuffer->handle();
744  BindFramebuffer(nullptr);
745  }
746  Flush();
747  return framebuffer;
748  }
749  LOG(VB_GENERAL, LOG_ERR, "Failed to create framebuffer object");
750  delete framebuffer;
751  return nullptr;
752 }
753 
754 MythGLTexture* MythRenderOpenGL::CreateFramebufferTexture(QOpenGLFramebufferObject *Framebuffer)
755 {
756  if (!Framebuffer)
757  return nullptr;
758 
759  auto *texture = new MythGLTexture(Framebuffer->texture());
760  texture->m_size = texture->m_totalSize = Framebuffer->size();
761  texture->m_vbo = CreateVBO(kVertexSize);
762  texture->m_flip = false;
763  return texture;
764 }
765 
766 void MythRenderOpenGL::DeleteFramebuffer(QOpenGLFramebufferObject *Framebuffer)
767 {
768  if (Framebuffer)
769  {
770  makeCurrent();
771  delete Framebuffer;
772  doneCurrent();
773  }
774 }
775 
776 void MythRenderOpenGL::BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
777 {
778  if ((Framebuffer && Framebuffer->handle() == m_activeFramebuffer) ||
779  (!Framebuffer && defaultFramebufferObject() == m_activeFramebuffer))
780  return;
781 
782  makeCurrent();
783  if (Framebuffer == nullptr)
784  {
785  QOpenGLFramebufferObject::bindDefault();
786  m_activeFramebuffer = defaultFramebufferObject();
787  }
788  else
789  {
790  Framebuffer->bind();
791  m_activeFramebuffer = Framebuffer->handle();
792  }
793  doneCurrent();
794 }
795 
797 {
798  makeCurrent();
799  glClear(GL_COLOR_BUFFER_BIT);
800  doneCurrent();
801 }
802 
803 void MythRenderOpenGL::DrawBitmap(MythGLTexture *Texture, QOpenGLFramebufferObject *Target,
804  const QRect &Source, const QRect &Destination,
805  QOpenGLShaderProgram *Program, int Alpha)
806 {
807  makeCurrent();
808 
809  if (!Texture || (Texture && !((Texture->m_texture || Texture->m_textureId) && Texture->m_vbo)))
810  return;
811 
812  if (Program == nullptr)
814 
815  BindFramebuffer(Target);
816  SetShaderProjection(Program);
817 
818  GLenum textarget = Texture->m_target;
819  Program->setUniformValue("s_texture0", 0);
821  if (Texture->m_texture)
822  Texture->m_texture->bind();
823  else
824  glBindTexture(textarget, Texture->m_textureId);
825 
826  QOpenGLBuffer* buffer = Texture->m_vbo;
827  buffer->bind();
828  if (UpdateTextureVertices(Texture, Source, Destination, 0))
829  {
831  {
832  void* target = buffer->map(QOpenGLBuffer::WriteOnly);
833  if (target)
834  memcpy(target, Texture->m_vertexData, kVertexSize);
835  buffer->unmap();
836  }
837  else
838  {
839  buffer->write(0, Texture->m_vertexData, kVertexSize);
840  }
841  }
842 
843  glEnableVertexAttribArray(VERTEX_INDEX);
844  glEnableVertexAttribArray(TEXTURE_INDEX);
845  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
846  glVertexAttrib4f(COLOR_INDEX, 1.0F, 1.0F, 1.0F, Alpha / 255.0F);
847  glVertexAttribPointerI(TEXTURE_INDEX, TEXTURE_SIZE, GL_FLOAT, GL_FALSE, TEXTURE_SIZE * sizeof(GLfloat), kTextureOffset);
848  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
849  glDisableVertexAttribArray(TEXTURE_INDEX);
850  glDisableVertexAttribArray(VERTEX_INDEX);
851  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
852  doneCurrent();
853 }
854 
855 void MythRenderOpenGL::DrawBitmap(MythGLTexture **Textures, uint TextureCount,
856  QOpenGLFramebufferObject *Target,
857  const QRect &Source, const QRect &Destination,
858  QOpenGLShaderProgram *Program,
859  int Rotation)
860 {
861  if (!Textures || !TextureCount)
862  return;
863 
864  makeCurrent();
865  BindFramebuffer(Target);
866 
867  if (Program == nullptr)
869 
870  MythGLTexture* first = Textures[0];
871  if (!first || (first && !((first->m_texture || first->m_textureId) && first->m_vbo)))
872  return;
873 
874  SetShaderProjection(Program);
875 
876  GLenum textarget = first->m_target;
877  uint active_tex = 0;
878  for (uint i = 0; i < TextureCount; i++)
879  {
880  QString uniform = QString("s_texture%1").arg(active_tex);
881  Program->setUniformValue(qPrintable(uniform), active_tex);
882  ActiveTexture(GL_TEXTURE0 + active_tex++);
883  if (Textures[i]->m_texture)
884  Textures[i]->m_texture->bind();
885  else
886  glBindTexture(textarget, Textures[i]->m_textureId);
887  }
888 
889  QOpenGLBuffer* buffer = first->m_vbo;
890  buffer->bind();
891  if (UpdateTextureVertices(first, Source, Destination, Rotation))
892  {
894  {
895  void* target = buffer->map(QOpenGLBuffer::WriteOnly);
896  if (target)
897  memcpy(target, first->m_vertexData, kVertexSize);
898  buffer->unmap();
899  }
900  else
901  {
902  buffer->write(0, first->m_vertexData, kVertexSize);
903  }
904  }
905 
906  glEnableVertexAttribArray(VERTEX_INDEX);
907  glEnableVertexAttribArray(TEXTURE_INDEX);
908  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
909  glVertexAttrib4f(COLOR_INDEX, 1.0, 1.0, 1.0, 1.0);
910  glVertexAttribPointerI(TEXTURE_INDEX, TEXTURE_SIZE, GL_FLOAT, GL_FALSE, TEXTURE_SIZE * sizeof(GLfloat), kTextureOffset);
911  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
912  glDisableVertexAttribArray(TEXTURE_INDEX);
913  glDisableVertexAttribArray(VERTEX_INDEX);
914  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
915  doneCurrent();
916 }
917 
918 static const float kLimitedRangeOffset = (16.0F / 255.0F);
919 static const float kLimitedRangeScale = (219.0F / 255.0F);
920 
922 void MythRenderOpenGL::ClearRect(QOpenGLFramebufferObject *Target, const QRect &Area, int Color)
923 {
924  makeCurrent();
925  BindFramebuffer(Target);
926  glEnableVertexAttribArray(VERTEX_INDEX);
927 
928  // Set the fill color
929  float color = m_fullRange ? Color / 255.0F : (Color * kLimitedRangeScale) + kLimitedRangeOffset;
930  glVertexAttrib4f(COLOR_INDEX, color, color, color, 255.0F);
932 
933  GetCachedVBO(GL_TRIANGLE_STRIP, Area);
934  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
935  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
936 
937  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
938  glDisableVertexAttribArray(VERTEX_INDEX);
939  doneCurrent();
940 }
941 
942 void MythRenderOpenGL::DrawRect(QOpenGLFramebufferObject *Target,
943  const QRect &Area, const QBrush &FillBrush,
944  const QPen &LinePen, int Alpha)
945 {
946  DrawRoundRect(Target, Area, 1, FillBrush, LinePen, Alpha);
947 }
948 
949 void MythRenderOpenGL::DrawRoundRect(QOpenGLFramebufferObject *Target,
950  const QRect &Area, int CornerRadius,
951  const QBrush &FillBrush,
952  const QPen &LinePen, int Alpha)
953 {
954  makeCurrent();
955  BindFramebuffer(Target);
956 
957  int lineWidth = LinePen.width();
958  int halfline = lineWidth / 2;
959  int rad = CornerRadius - halfline;
960 
961  if ((Area.width() / 2) < rad)
962  rad = Area.width() / 2;
963 
964  if ((Area.height() / 2) < rad)
965  rad = Area.height() / 2;
966  int dia = rad * 2;
967 
968  QRect r(Area.left(), Area.top(), Area.width(), Area.height());
969 
970  QRect tl(r.left(), r.top(), rad, rad);
971  QRect tr(r.left() + r.width() - rad, r.top(), rad, rad);
972  QRect bl(r.left(), r.top() + r.height() - rad, rad, rad);
973  QRect br(r.left() + r.width() - rad, r.top() + r.height() - rad, rad, rad);
974 
975  glEnableVertexAttribArray(VERTEX_INDEX);
976 
977  if (FillBrush.style() != Qt::NoBrush)
978  {
979  // Get the shaders
980  QOpenGLShaderProgram* elip = m_defaultPrograms[kShaderCircle];
981  QOpenGLShaderProgram* fill = m_defaultPrograms[kShaderSimple];
982 
983  // Set the fill color
984  if (m_fullRange)
985  {
986  glVertexAttrib4f(COLOR_INDEX,
987  FillBrush.color().red() / 255.0F,
988  FillBrush.color().green() / 255.0F,
989  FillBrush.color().blue() / 255.0F,
990  (FillBrush.color().alpha() / 255.0F) * (Alpha / 255.0F));
991  }
992  else
993  {
994  glVertexAttrib4f(COLOR_INDEX,
995  (FillBrush.color().red() * kLimitedRangeScale) + kLimitedRangeOffset,
996  (FillBrush.color().blue() * kLimitedRangeScale) + kLimitedRangeOffset,
997  (FillBrush.color().green() * kLimitedRangeScale) + kLimitedRangeOffset,
998  (FillBrush.color().alpha() / 255.0F) * (Alpha / 255.0F));
999  }
1000 
1001  // Set the radius
1002  m_parameters(2,0) = rad;
1003  m_parameters(3,0) = rad - 1.0F;
1004 
1005  // Enable the Circle shader
1006  SetShaderProjection(elip);
1007 
1008  // Draw the top left segment
1009  m_parameters(0,0) = tl.left() + rad;
1010  m_parameters(1,0) = tl.top() + rad;
1011 
1012  SetShaderProgramParams(elip, m_parameters, "u_parameters");
1013  GetCachedVBO(GL_TRIANGLE_STRIP, tl);
1014  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1015  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1016 
1017  // Draw the top right segment
1018  m_parameters(0,0) = tr.left();
1019  m_parameters(1,0) = tr.top() + rad;
1020  SetShaderProgramParams(elip, m_parameters, "u_parameters");
1021  GetCachedVBO(GL_TRIANGLE_STRIP, tr);
1022  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1023  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1024 
1025  // Draw the bottom left segment
1026  m_parameters(0,0) = bl.left() + rad;
1027  m_parameters(1,0) = bl.top();
1028  SetShaderProgramParams(elip, m_parameters, "u_parameters");
1029  GetCachedVBO(GL_TRIANGLE_STRIP, bl);
1030  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1031  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1032 
1033  // Draw the bottom right segment
1034  m_parameters(0,0) = br.left();
1035  m_parameters(1,0) = br.top();
1036  SetShaderProgramParams(elip, m_parameters, "u_parameters");
1037  GetCachedVBO(GL_TRIANGLE_STRIP, br);
1038  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1039  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1040 
1041  // Fill the remaining areas
1042  QRect main(r.left() + rad, r.top(), r.width() - dia, r.height());
1043  QRect left(r.left(), r.top() + rad, rad, r.height() - dia);
1044  QRect right(r.left() + r.width() - rad, r.top() + rad, rad, r.height() - dia);
1045 
1046  SetShaderProjection(fill);
1047 
1048  GetCachedVBO(GL_TRIANGLE_STRIP, main);
1049  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1050  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1051  GetCachedVBO(GL_TRIANGLE_STRIP, left);
1052  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1053  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1054  GetCachedVBO(GL_TRIANGLE_STRIP, right);
1055  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1056  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1057  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
1058  }
1059 
1060  if (LinePen.style() != Qt::NoPen)
1061  {
1062  // Get the shaders
1063  QOpenGLShaderProgram* edge = m_defaultPrograms[kShaderCircleEdge];
1064  QOpenGLShaderProgram* vline = m_defaultPrograms[kShaderVertLine];
1065  QOpenGLShaderProgram* hline = m_defaultPrograms[kShaderHorizLine];
1066 
1067  // Set the line color
1068  if (m_fullRange)
1069  {
1070  glVertexAttrib4f(COLOR_INDEX,
1071  LinePen.color().red() / 255.0F,
1072  LinePen.color().green() / 255.0F,
1073  LinePen.color().blue() / 255.0F,
1074  (LinePen.color().alpha() / 255.0F) * (Alpha / 255.0F));
1075  }
1076  else
1077  {
1078  glVertexAttrib4f(COLOR_INDEX,
1079  (LinePen.color().red() * kLimitedRangeScale) + kLimitedRangeOffset,
1080  (LinePen.color().blue() * kLimitedRangeScale) + kLimitedRangeOffset,
1081  (LinePen.color().green() * kLimitedRangeScale) + kLimitedRangeOffset,
1082  (FillBrush.color().alpha() / 255.0F) * (Alpha / 255.0F));
1083  }
1084 
1085  // Set the radius and width
1086  m_parameters(2,0) = rad - lineWidth / 2.0F;
1087  m_parameters(3,0) = lineWidth / 2.0F;
1088 
1089  // Enable the edge shader
1090  SetShaderProjection(edge);
1091 
1092  // Draw the top left edge segment
1093  m_parameters(0,0) = tl.left() + rad;
1094  m_parameters(1,0) = tl.top() + rad;
1095  SetShaderProgramParams(edge, m_parameters, "u_parameters");
1096  GetCachedVBO(GL_TRIANGLE_STRIP, tl);
1097  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1098  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1099 
1100  // Draw the top right edge segment
1101  m_parameters(0,0) = tr.left();
1102  m_parameters(1,0) = tr.top() + rad;
1103  SetShaderProgramParams(edge, m_parameters, "u_parameters");
1104  GetCachedVBO(GL_TRIANGLE_STRIP, tr);
1105  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat),kVertexOffset);
1106  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1107 
1108  // Draw the bottom left edge segment
1109  m_parameters(0,0) = bl.left() + rad;
1110  m_parameters(1,0) = bl.top();
1111  SetShaderProgramParams(edge, m_parameters, "u_parameters");
1112  GetCachedVBO(GL_TRIANGLE_STRIP, bl);
1113  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1114  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1115 
1116  // Draw the bottom right edge segment
1117  m_parameters(0,0) = br.left();
1118  m_parameters(1,0) = br.top();
1119  SetShaderProgramParams(edge, m_parameters, "u_parameters");
1120  GetCachedVBO(GL_TRIANGLE_STRIP, br);
1121  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1122  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1123 
1124  // Vertical lines
1125  SetShaderProjection(vline);
1126 
1127  m_parameters(1,0) = lineWidth / 2.0F;
1128  QRect vl(r.left(), r.top() + rad, lineWidth, r.height() - dia);
1129 
1130  // Draw the left line segment
1131  m_parameters(0,0) = vl.left() + lineWidth;
1132  SetShaderProgramParams(vline, m_parameters, "u_parameters");
1133  GetCachedVBO(GL_TRIANGLE_STRIP, vl);
1134  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1135  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1136 
1137  // Draw the right line segment
1138  vl.translate(r.width() - lineWidth, 0);
1139  m_parameters(0,0) = vl.left();
1140  SetShaderProgramParams(vline, m_parameters, "u_parameters");
1141  GetCachedVBO(GL_TRIANGLE_STRIP, vl);
1142  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1143  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1144 
1145  // Horizontal lines
1146  SetShaderProjection(hline);
1147  QRect hl(r.left() + rad, r.top(), r.width() - dia, lineWidth);
1148 
1149  // Draw the top line segment
1150  m_parameters(0,0) = hl.top() + lineWidth;
1151  SetShaderProgramParams(hline, m_parameters, "u_parameters");
1152  GetCachedVBO(GL_TRIANGLE_STRIP, hl);
1153  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1154  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1155 
1156  // Draw the bottom line segment
1157  hl.translate(0, r.height() - lineWidth);
1158  m_parameters(0,0) = hl.top();
1159  SetShaderProgramParams(hline, m_parameters, "u_parameters");
1160  GetCachedVBO(GL_TRIANGLE_STRIP, hl);
1161  glVertexAttribPointerI(VERTEX_INDEX, VERTEX_SIZE, GL_FLOAT, GL_FALSE, VERTEX_SIZE * sizeof(GLfloat), kVertexOffset);
1162  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1163  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
1164  }
1165  glDisableVertexAttribArray(VERTEX_INDEX);
1166  doneCurrent();
1167 }
1168 
1169 inline void MythRenderOpenGL::glVertexAttribPointerI(GLuint Index, GLint Size, GLenum Type, GLboolean Normalize,
1170  GLsizei Stride, const GLuint Value)
1171 {
1172 #pragma GCC diagnostic push
1173 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
1174  glVertexAttribPointer(Index, Size, Type, Normalize, Stride, reinterpret_cast<const char *>(Value));
1175 #pragma GCC diagnostic pop
1176 }
1177 
1179 {
1180  SetBlend(true);
1181  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1182  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1183  glDisable(GL_DEPTH_TEST);
1184  glDepthMask(GL_FALSE);
1185  glDisable(GL_CULL_FACE);
1186  glClearColor(0.0F, 0.0F, 0.0F, 0.0F);
1187  glClear(GL_COLOR_BUFFER_BIT);
1188  QOpenGLFramebufferObject::bindDefault();
1189  m_activeFramebuffer = defaultFramebufferObject();
1190  Flush();
1191 }
1192 
1193 QFunctionPointer MythRenderOpenGL::GetProcAddress(const QString &Proc) const
1194 {
1195  static const QString kExts[4] = { "", "ARB", "EXT", "OES" };
1196  QFunctionPointer result = nullptr;
1197  for (const auto & ext : kExts)
1198  {
1199  result = getProcAddress((Proc + ext).toLocal8Bit().constData());
1200  if (result)
1201  break;
1202  }
1203  if (result == nullptr)
1204  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Extension not found: %1").arg(Proc));
1205  return result;
1206 }
1207 
1208 QOpenGLBuffer* MythRenderOpenGL::CreateVBO(int Size, bool Release /*=true*/)
1209 {
1210  OpenGLLocker locker(this);
1211  auto* buffer = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
1212  if (buffer->create())
1213  {
1214  buffer->setUsagePattern(QOpenGLBuffer::StreamDraw);
1215  buffer->bind();
1216  buffer->allocate(Size);
1217  if (Release)
1218  QOpenGLBuffer::release(QOpenGLBuffer::VertexBuffer);
1219  return buffer;
1220  }
1221  delete buffer;
1222  return nullptr;
1223 }
1224 
1226 {
1227  OpenGLLocker locker(this);
1228  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
1229  logDebugMarker("RENDER_RELEASE_START");
1231  ExpireVertices();
1232  ExpireVBOS();
1233  if (m_coreProfile && m_vao)
1234  {
1235  QOpenGLExtraFunctions extra(nullptr);
1236  extra.initializeOpenGLFunctions();
1237  extra.glDeleteVertexArrays(1, &m_vao);
1238  }
1239  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
1240  logDebugMarker("RENDER_RELEASE_END");
1241  delete m_openglDebugger;
1242  m_openglDebugger = nullptr;
1243  Flush();
1244 
1245  if (!m_cachedVertices.empty())
1246  LOG(VB_GENERAL, LOG_ERR, LOC + QString(" %1 unexpired vertices").arg(m_cachedVertices.size()));
1247 
1248  if (!m_cachedVBOS.empty())
1249  LOG(VB_GENERAL, LOG_ERR, LOC + QString(" %1 unexpired VBOs").arg(m_cachedVertices.size()));
1250 }
1251 
1253 {
1254  QStringList result;
1255  result.append(tr("QPA platform") + "\t: " + QGuiApplication::platformName());
1256  result.append(tr("OpenGL vendor") + "\t: " + reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
1257  result.append(tr("OpenGL renderer") + "\t: " + reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
1258  result.append(tr("OpenGL version") + "\t: " + reinterpret_cast<const char*>(glGetString(GL_VERSION)));
1259  result.append(tr("Maximum depth") + "\t: " + QString::number(GetColorDepth()));
1260  return result;
1261 }
1262 
1264  const QRect &Destination, int Rotation)
1265 {
1266  if (!Texture || (Texture && Texture->m_size.isEmpty()))
1267  return false;
1268 
1269  if ((Texture->m_source == Source) && (Texture->m_destination == Destination) &&
1270  (Texture->m_rotation == Rotation))
1271  return false;
1272 
1273  Texture->m_source = Source;
1274  Texture->m_destination = Destination;
1275  Texture->m_rotation = Rotation;
1276 
1277  GLfloat *data = Texture->m_vertexData;
1278  QSize size = Texture->m_size;
1279 
1280  int width = Texture->m_crop ? min(Source.width(), size.width()) : Source.width();
1281  int height = Texture->m_crop ? min(Source.height(), size.height()) : Source.height();
1282 
1283  if (Texture->m_target != QOpenGLTexture::TargetRectangle)
1284  {
1285  data[0 + TEX_OFFSET] = Source.left() / static_cast<GLfloat>(size.width());
1286  data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET] = (Source.top() + height) / static_cast<GLfloat>(size.height());
1287  data[6 + TEX_OFFSET] = (Source.left() + width) / static_cast<GLfloat>(size.width());
1288  data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET] = Source.top() / static_cast<GLfloat>(size.height());
1289  }
1290  else
1291  {
1292  data[0 + TEX_OFFSET] = Source.left();
1293  data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET] = (Source.top() + height);
1294  data[6 + TEX_OFFSET] = (Source.left() + width);
1295  data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET] = Source.top();
1296  }
1297 
1298  data[2 + TEX_OFFSET] = data[0 + TEX_OFFSET];
1299  data[3 + TEX_OFFSET] = data[7 + TEX_OFFSET];
1300  data[4 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1301  data[5 + TEX_OFFSET] = data[1 + TEX_OFFSET];
1302 
1303  width = Texture->m_crop ? min(width, Destination.width()) : Destination.width();
1304  height = Texture->m_crop ? min(height, Destination.height()) : Destination.height();
1305 
1306  data[2] = data[0] = Destination.left();
1307  data[5] = data[1] = Destination.top();
1308  data[4] = data[6] = Destination.left() + width;
1309  data[3] = data[7] = Destination.top() + height;
1310 
1311  if (Texture->m_rotation != 0)
1312  {
1313  GLfloat temp = NAN;
1314  if (Texture->m_rotation == 90)
1315  {
1316  temp = data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET];
1317  data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET] = data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET];
1318  data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET] = temp;
1319  data[2 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1320  data[4 + TEX_OFFSET] = data[0 + TEX_OFFSET];
1321  }
1322  else if (Texture->m_rotation == -90)
1323  {
1324  temp = data[0 + TEX_OFFSET];
1325  data[0 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1326  data[6 + TEX_OFFSET] = temp;
1327  data[3 + TEX_OFFSET] = data[1 + TEX_OFFSET];
1328  data[5 + TEX_OFFSET] = data[7 + TEX_OFFSET];
1329  }
1330  else if (abs(Texture->m_rotation) == 180)
1331  {
1332  temp = data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET];
1333  data[(Texture->m_flip ? 7 : 1) + TEX_OFFSET] = data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET];
1334  data[(Texture->m_flip ? 1 : 7) + TEX_OFFSET] = temp;
1335  data[3 + TEX_OFFSET] = data[7 + TEX_OFFSET];
1336  data[5 + TEX_OFFSET] = data[1 + TEX_OFFSET];
1337  temp = data[0 + TEX_OFFSET];
1338  data[0 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1339  data[6 + TEX_OFFSET] = temp;
1340  data[2 + TEX_OFFSET] = data[0 + TEX_OFFSET];
1341  data[4 + TEX_OFFSET] = data[6 + TEX_OFFSET];
1342  }
1343  }
1344 
1345  return true;
1346 }
1347 
1348 GLfloat* MythRenderOpenGL::GetCachedVertices(GLuint Type, const QRect &Area)
1349 {
1350  uint64_t ref = (static_cast<uint64_t>(Area.left()) & 0xfff) +
1351  ((static_cast<uint64_t>(Area.top()) & 0xfff) << 12) +
1352  ((static_cast<uint64_t>(Area.width()) & 0xfff) << 24) +
1353  ((static_cast<uint64_t>(Area.height()) & 0xfff) << 36) +
1354  ((static_cast<uint64_t>(Type & 0xfff)) << 48);
1355 
1356  if (m_cachedVertices.contains(ref))
1357  {
1358  m_vertexExpiry.removeOne(ref);
1359  m_vertexExpiry.append(ref);
1360  return m_cachedVertices[ref];
1361  }
1362 
1363  auto *vertices = new GLfloat[8];
1364 
1365  vertices[2] = vertices[0] = Area.left();
1366  vertices[5] = vertices[1] = Area.top();
1367  vertices[4] = vertices[6] = Area.left() + Area.width();
1368  vertices[3] = vertices[7] = Area.top() + Area.height();
1369 
1370  if (Type == GL_LINE_LOOP)
1371  {
1372  vertices[7] = vertices[1];
1373  vertices[5] = vertices[3];
1374  }
1375 
1376  m_cachedVertices.insert(ref, vertices);
1377  m_vertexExpiry.append(ref);
1379 
1380  return vertices;
1381 }
1382 
1384 {
1385  while (m_vertexExpiry.size() > Max)
1386  {
1387  uint64_t ref = m_vertexExpiry.first();
1388  m_vertexExpiry.removeFirst();
1389  GLfloat *vertices = nullptr;
1390  if (m_cachedVertices.contains(ref))
1391  vertices = m_cachedVertices.value(ref);
1392  m_cachedVertices.remove(ref);
1393  delete [] vertices;
1394  }
1395 }
1396 
1397 void MythRenderOpenGL::GetCachedVBO(GLuint Type, const QRect &Area)
1398 {
1399  uint64_t ref = (static_cast<uint64_t>(Area.left()) & 0xfff) +
1400  ((static_cast<uint64_t>(Area.top()) & 0xfff) << 12) +
1401  ((static_cast<uint64_t>(Area.width()) & 0xfff) << 24) +
1402  ((static_cast<uint64_t>(Area.height()) & 0xfff) << 36) +
1403  ((static_cast<uint64_t>(Type & 0xfff)) << 48);
1404 
1405  if (m_cachedVBOS.contains(ref))
1406  {
1407  m_vboExpiry.removeOne(ref);
1408  m_vboExpiry.append(ref);
1409  m_cachedVBOS.value(ref)->bind();
1410  return;
1411  }
1412 
1413  GLfloat *vertices = GetCachedVertices(Type, Area);
1414  QOpenGLBuffer *vbo = CreateVBO(kTextureOffset, false);
1415  m_cachedVBOS.insert(ref, vbo);
1416  m_vboExpiry.append(ref);
1417 
1419  {
1420  void* target = vbo->map(QOpenGLBuffer::WriteOnly);
1421  if (target)
1422  memcpy(target, vertices, kTextureOffset);
1423  vbo->unmap();
1424  }
1425  else
1426  {
1427  vbo->write(0, vertices, kTextureOffset);
1428  }
1430 }
1431 
1433 {
1434  while (m_vboExpiry.size() > Max)
1435  {
1436  uint64_t ref = m_vboExpiry.first();
1437  m_vboExpiry.removeFirst();
1438  if (m_cachedVBOS.contains(ref))
1439  {
1440  QOpenGLBuffer *vbo = m_cachedVBOS.value(ref);
1441  delete vbo;
1442  m_cachedVBOS.remove(ref);
1443  }
1444  }
1445 }
1446 
1447 int MythRenderOpenGL::GetBufferSize(QSize Size, QOpenGLTexture::PixelFormat Format, QOpenGLTexture::PixelType Type)
1448 {
1449  int bytes = 0;
1450  int bpp = 0;;
1451 
1452  switch (Format)
1453  {
1454  case QOpenGLTexture::RGBA_Integer:
1455  case QOpenGLTexture::BGRA_Integer:
1456  case QOpenGLTexture::BGRA:
1457  case QOpenGLTexture::RGBA: bpp = 4; break;
1458  case QOpenGLTexture::RGB_Integer:
1459  case QOpenGLTexture::BGR_Integer:
1460  case QOpenGLTexture::BGR:
1461  case QOpenGLTexture::RGB: bpp = 3; break;
1462  case QOpenGLTexture::RG_Integer:
1463  case QOpenGLTexture::RG: bpp = 2; break;
1464  case QOpenGLTexture::Red:
1465  case QOpenGLTexture::Red_Integer:
1466  case QOpenGLTexture::Alpha:
1467  case QOpenGLTexture::Luminance: bpp = 1; break;
1468  default: break; // unsupported
1469  }
1470 
1471  switch (Type)
1472  {
1473  case QOpenGLTexture::Int8: bytes = sizeof(GLbyte); break;
1474  case QOpenGLTexture::UInt8: bytes = sizeof(GLubyte); break;
1475  case QOpenGLTexture::Int16: bytes = sizeof(GLshort); break;
1476  case QOpenGLTexture::UInt16: bytes = sizeof(GLushort); break;
1477  case QOpenGLTexture::Int32: bytes = sizeof(GLint); break;
1478  case QOpenGLTexture::UInt32: bytes = sizeof(GLuint); break;
1479  case QOpenGLTexture::Float32: bytes = sizeof(GLfloat); break;
1480  case QOpenGLTexture::UInt32_RGB10A2: bytes = sizeof(GLuint); break;
1481  default: break; // unsupported
1482  }
1483 
1484  if (!bpp || !bytes || Size.isEmpty())
1485  return 0;
1486 
1487  return Size.width() * Size.height() * bpp * bytes;
1488 }
1489 
1490 void MythRenderOpenGL::PushTransformation(const UIEffects &fx, QPointF &center)
1491 {
1492  QMatrix4x4 newtop = m_transforms.top();
1493  if (fx.m_hzoom != 1.0F || fx.m_vzoom != 1.0F || fx.m_angle != 0.0F)
1494  {
1495  newtop.translate(static_cast<GLfloat>(center.x()), static_cast<GLfloat>(center.y()));
1496  newtop.scale(fx.m_hzoom, fx.m_vzoom);
1497  newtop.rotate(fx.m_angle, 0, 0, 1);
1498  newtop.translate(static_cast<GLfloat>(-center.x()), static_cast<GLfloat>(-center.y()));
1499  }
1500  m_transforms.push(newtop);
1501 }
1502 
1504 {
1505  m_transforms.pop();
1506 }
1507 
1508 inline QOpenGLShaderProgram* ShaderError(QOpenGLShaderProgram *Shader, const QString &Source)
1509 {
1510  QString type = Source.isEmpty() ? "Shader link" : "Shader compile";
1511  LOG(VB_GENERAL, LOG_ERR, LOC + QString("%1 error").arg(type));
1512  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Log:"));
1513  LOG(VB_GENERAL, LOG_ERR, "\n" + Shader->log());
1514  if (!Source.isEmpty())
1515  {
1516  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Source:"));
1517  LOG(VB_GENERAL, LOG_ERR, "\n" + Source);
1518  }
1519  delete Shader;
1520  return nullptr;
1521 }
1522 
1523 QOpenGLShaderProgram *MythRenderOpenGL::CreateShaderProgram(const QString &Vertex, const QString &Fragment)
1524 {
1525  if (!(m_features & Shaders))
1526  return nullptr;
1527 
1528  OpenGLLocker locker(this);
1529  QString vertex = Vertex.isEmpty() ? kDefaultVertexShader : Vertex;
1530  QString fragment = Fragment.isEmpty() ? kDefaultFragmentShader: Fragment;
1531  auto *program = new QOpenGLShaderProgram();
1532  if (!program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertex))
1533  return ShaderError(program, vertex);
1534  if (!program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragment))
1535  return ShaderError(program, fragment);
1536  if (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_DEBUG))
1537  {
1538  QList<QOpenGLShader*> shaders = program->shaders();
1539  foreach (QOpenGLShader* shader, shaders)
1540  LOG(VB_GENERAL, LOG_DEBUG, "\n" + shader->sourceCode());
1541  }
1542  program->bindAttributeLocation("a_position", VERTEX_INDEX);
1543  program->bindAttributeLocation("a_color", COLOR_INDEX);
1544  program->bindAttributeLocation("a_texcoord0", TEXTURE_INDEX);
1545  if (!program->link())
1546  return ShaderError(program, "");
1547  return program;
1548 }
1549 
1550 void MythRenderOpenGL::DeleteShaderProgram(QOpenGLShaderProgram *Program)
1551 {
1552  makeCurrent();
1553  delete Program;
1554  m_cachedMatrixUniforms.clear();
1555  m_activeProgram = nullptr;
1556  m_cachedUniformLocations.remove(Program);
1557  doneCurrent();
1558 }
1559 
1560 bool MythRenderOpenGL::EnableShaderProgram(QOpenGLShaderProgram* Program)
1561 {
1562  if (!Program)
1563  return false;
1564 
1565  if (m_activeProgram == Program)
1566  return true;
1567 
1568  makeCurrent();
1569  Program->bind();
1570  m_activeProgram = Program;
1571  doneCurrent();
1572  return true;
1573 }
1574 
1575 void MythRenderOpenGL::SetShaderProjection(QOpenGLShaderProgram *Program)
1576 {
1577  if (Program)
1578  {
1579  SetShaderProgramParams(Program, m_projection, "u_projection");
1580  SetShaderProgramParams(Program, m_transforms.top(), "u_transform");
1581  }
1582 }
1583 
1584 void MythRenderOpenGL::SetShaderProgramParams(QOpenGLShaderProgram *Program, const QMatrix4x4 &Value, const char *Uniform)
1585 {
1586  OpenGLLocker locker(this);
1587  if (!Uniform || !EnableShaderProgram(Program))
1588  return;
1589 
1590  // Uniform value cacheing
1591  QString tag = QStringLiteral("%1-%2").arg(Program->programId()).arg(Uniform);
1592  QHash<QString,QMatrix4x4>::iterator it = m_cachedMatrixUniforms.find(tag);
1593  if (it == m_cachedMatrixUniforms.end())
1594  m_cachedMatrixUniforms.insert(tag, Value);
1595  else if (!qFuzzyCompare(Value, it.value()))
1596  it.value() = Value;
1597  else
1598  return;
1599 
1600  // Uniform location cacheing
1601  QByteArray uniform(Uniform);
1602  GLint location = 0;
1603  QHash<QByteArray, GLint> &uniforms = m_cachedUniformLocations[Program];
1604  if (uniforms.contains(uniform))
1605  {
1606  location = uniforms[uniform];
1607  }
1608  else
1609  {
1610  location = Program->uniformLocation(Uniform);
1611  uniforms.insert(uniform, location);
1612  }
1613 
1614  Program->setUniformValue(location, Value);
1615 }
1616 
1618 {
1628 }
1629 
1631 {
1632  for (auto & program : m_defaultPrograms)
1633  {
1634  DeleteShaderProgram(program);
1635  program = nullptr;
1636  }
1637 }
1638 
1640 {
1641  m_projection.setToIdentity();
1642  m_projection.ortho(m_viewport);
1643 }
1644 
1645 bool MythRenderOpenGL::GetGPUMemory(int &Available, int &Total)
1646 {
1647  OpenGLLocker locker(this);
1649  {
1650  GLint kb = 0;
1651  glGetIntegerv(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &kb);
1652  Total = kb / 1024;
1654  Available = kb / 1024;
1655  return true;
1656  }
1657  return false;
1658 }
void SetTextureFilters(MythGLTexture *Texture, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
int GetExtraFeatures(void) const
static MythMainWindow * getMainWindow(bool useDB=true)
Return the existing main window, or create one.
QOpenGLShaderProgram * CreateShaderProgram(const QString &Vertex, const QString &Fragment)
QOpenGLFramebufferObject * CreateFramebuffer(QSize &Size, GLenum InternalFormat=0)
void SetShaderProgramParams(QOpenGLShaderProgram *Program, const QMatrix4x4 &Value, const char *Uniform)
void SetBlend(bool enable)
VERBOSE_PREAMBLE Most true
Definition: verbosedefs.h:91
#define COLOR_INDEX
void DrawRoundRect(QOpenGLFramebufferObject *Target, const QRect &Area, int CornerRadius, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
void glVertexAttribPointerI(GLuint Index, GLint Size, GLenum Type, GLboolean Normalize, GLsizei Stride, GLuint Value)
bool EnableShaderProgram(QOpenGLShaderProgram *Program)
#define TEXTURE_INDEX
static const float kLimitedRangeOffset
OpenGLLocker(MythRenderOpenGL *Render)
static int GetBufferSize(QSize Size, QOpenGLTexture::PixelFormat Format, QOpenGLTexture::PixelType Type)
QMap< uint64_t, QOpenGLBuffer * > m_cachedVBOS
static int GetTextureDataSize(MythGLTexture *Texture)
QList< uint64_t > m_vertexExpiry
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
MythGLTexture(QOpenGLTexture *Texture)
RenderType Type(void) const
#define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
QOpenGLFunctions::OpenGLFeatures m_features
static const GLuint kVertexSize
#define GLYesNo(arg)
QFunctionPointer GetProcAddress(const QString &Proc) const
static const QString kDefaultFragmentShaderLimited
static const float kLimitedRangeScale
void logDebugMarker(const QString &Message)
~MythRenderOpenGL() override
void DeleteDefaultShaders(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define VERTEX_SIZE
QMatrix4x4 m_parameters
Definition: graphic.h:6
QSize GetTextureSize(const QSize &size, bool Normalised)
void DeleteShaderProgram(QOpenGLShaderProgram *Program)
QOpenGLShaderProgram * m_defaultPrograms[kShaderCount]
static guint32 * tmp
Definition: goom_core.c:35
void ExpireVBOS(int Max=0)
#define GL_TEXTURE0
void PushTransformation(const UIEffects &fx, QPointF &center)
QOpenGLShaderProgram * ShaderError(QOpenGLShaderProgram *Shader, const QString &Source)
void SetBackground(int r, int g, int b, int a)
#define TEX_OFFSET
MythGLTexture * CreateFramebufferTexture(QOpenGLFramebufferObject *Framebuffer)
GLfloat m_vertexData[16]
static const GLuint kVertexOffset
QList< uint64_t > m_vboExpiry
void GetCachedVBO(GLuint Type, const QRect &Area)
void SetShaderProjection(QOpenGLShaderProgram *Program)
QMap< uint64_t, GLfloat * > m_cachedVertices
QStack< QMatrix4x4 > m_transforms
MythGLTexture * CreateTextureFromQImage(QImage *Image)
QOpenGLFunctions::OpenGLFeatures GetFeatures(void) const
void DeleteFramebuffer(QOpenGLFramebufferObject *Framebuffer)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
int GetColorDepth(void) const
const T & Max(const T &x, const T &y)
Definition: upnputil.h:35
static const GLuint kTextureOffset
QOpenGLBuffer * m_vbo
static const QString kCircleEdgeFragmentShader
void messageLogged(const QOpenGLDebugMessage &Message)
QOpenGLShaderProgram * m_activeProgram
void DrawBitmap(MythGLTexture *Texture, QOpenGLFramebufferObject *Target, const QRect &Source, const QRect &Destination, QOpenGLShaderProgram *Program, int Alpha=255)
void DrawRect(QOpenGLFramebufferObject *Target, const QRect &Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
QOpenGLBuffer * CreateVBO(int Size, bool Release=true)
MythRenderOpenGL(const QSurfaceFormat &Format, QWidget *Widget)
unsigned int uint
Definition: compat.h:140
QHash< QString, QMatrix4x4 > m_cachedMatrixUniforms
void * GetEGLDisplay(void)
Definition: mythegl.cpp:80
void SetWidget(QWidget *Widget)
static const QString kSimpleVertexShader
MythRender * GetRenderDevice()
#define LOC
bool HasMythMainWindow(void)
static const QString kCircleFragmentShader
QMatrix4x4 m_projection
void PopTransformation(void)
void ExpireVertices(int Max=0)
void DeleteTexture(MythGLTexture *Texture)
void SetViewPort(const QRect &rect, bool viewportonly=false)
QOpenGLDebugLogger * m_openglDebugger
QStringList GetDescription(void) override
QOpenGLDebugMessage::Types m_openGLDebuggerFilter
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static const QString kHorizLineFragmentShader
MythRenderOpenGL * m_render
static QString Source(const QNetworkRequest &request)
Definition: netstream.cpp:137
static const QString kSimpleFragmentShader
static MythRenderOpenGL * Create(QWidget *Widget)
#define VERTEX_INDEX
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL
static const QString kDrawVertexShader
static const QString kVertLineFragmentShader
static MythRenderOpenGL * GetOpenGLRender(void)
void contextToBeDestroyed(void)
Definition: image.h:30
void ReleaseResources(void) override
void ActiveTexture(GLuint ActiveTex)
bool CreateDefaultShaders(void)
int GetMaxTextureSize(void) const
QHash< QOpenGLShaderProgram *, QHash< QByteArray, GLint > > m_cachedUniformLocations
static const QString kDefaultVertexShader
void ClearRect(QOpenGLFramebufferObject *Target, const QRect &Area, int Color)
An optimised method to clear a QRect to the given color.
static const QString kDefaultFragmentShader
int GetMaxTextureUnits(void) const
bool IsEGL(void)
Definition: mythegl.cpp:29
#define MAX_VERTEX_CACHE
unsigned char * m_data
#define TEXTURE_SIZE
#define GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX
GLfloat * GetCachedVertices(GLuint Type, const QRect &Area)
QOpenGLTexture * m_texture
void ClearFramebuffer(void)
bool GetGPUMemory(int &Available, int &Total)
bool IsRecommendedRenderer(void)
static bool UpdateTextureVertices(MythGLTexture *Texture, const QRect &Source, const QRect &Destination, int Rotation)