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