MythTV  master
mythpainteropengl.cpp
Go to the documentation of this file.
1 // Config header generated in base directory by configure
2 #include "config.h"
3 
4 // Qt
5 #include <QCoreApplication>
6 #include <QPainter>
7 
8 // MythTV
9 #include "mythrenderopengl.h"
10 #include "mythpainteropengl.h"
11 
12 using namespace std;
13 
15  : m_widget(Parent),
16  m_render(Render)
17 {
19 
20  if (!m_render)
21  LOG(VB_GENERAL, LOG_ERR, "OpenGL painter has no render device");
22 }
23 
25 {
26  if (!m_render)
27  return;
28  if (!m_render->IsReady())
29  return;
30  OpenGLLocker locker(m_render);
31  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
32  m_render->logDebugMarker("PAINTER_RELEASE_START");
33  Teardown();
35  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
36  m_render->logDebugMarker("PAINTER_RELEASE_END");
37 }
38 
40 {
41  OpenGLLocker locker(m_render);
42  ClearCache();
45  {
46  for (auto & buf : m_mappedBufferPool)
47  {
48  delete buf;
49  buf = nullptr;
50  }
52  }
53 
55 }
56 
58 {
59  if (!m_render || m_textureDeleteList.empty())
60  return;
61 
62  QMutexLocker gllocker(&m_textureDeleteLock);
63  OpenGLLocker locker(m_render);
64  while (!m_textureDeleteList.empty())
65  {
66  MythGLTexture *texture = m_textureDeleteList.front();
68  m_render->DeleteTexture(texture);
69  m_textureDeleteList.pop_front();
70  }
71 }
72 
74 {
75  LOG(VB_GENERAL, LOG_INFO, "Clearing OpenGL painter cache.");
76 
77  QMutexLocker locker(&m_textureDeleteLock);
78  QMapIterator<MythImage *, MythGLTexture*> it(m_imageToTextureMap);
79  while (it.hasNext())
80  {
81  it.next();
82  m_textureDeleteList.push_back(m_imageToTextureMap[it.key()]);
83  m_ImageExpireList.remove(it.key());
84  }
85  m_imageToTextureMap.clear();
86 }
87 
88 void MythOpenGLPainter::Begin(QPaintDevice *Parent)
89 {
90  MythPainter::Begin(Parent);
91 
92  if (!m_widget)
93  {
94  m_widget = dynamic_cast<QWidget *>(Parent);
95  if (!m_widget)
96  return;
97  }
98 
99  if (!m_render)
100  {
101  LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'Begin'");
102  return;
103  }
104 
106  {
108  // initialise the VBO pool
109  for (auto & buf : m_mappedBufferPool)
110  buf = m_render->CreateVBO(static_cast<int>(MythRenderOpenGL::kVertexSize));
111  }
112 
113  // check if we need to adjust cache sizes
114  if (m_lastSize != m_widget->size())
115  {
116  // This will scale the cache depending on the resolution in use
117  static const int s_onehd = 1920 * 1080;
118  static const int s_basesize = 64;
119  m_lastSize = m_widget->size();
120  float hdscreens = (static_cast<float>(m_lastSize.width() + 1) * m_lastSize.height()) / s_onehd;
121  int cpu = qMax(static_cast<int>(hdscreens * s_basesize), s_basesize);
122  int gpu = cpu * 3 / 2;
123  SetMaximumCacheSizes(gpu, cpu);
124  }
125 
126  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
127  m_render->logDebugMarker("PAINTER_FRAME_START");
128 
129  DeleteTextures();
131 
132  if (m_target || m_swapControl)
133  {
135  m_render->SetViewPort(QRect(0, 0, m_widget->width(), m_widget->height()));
136  m_render->SetBackground(0, 0, 0, 0);
138  }
139 }
140 
142 {
143  if (!m_render)
144  {
145  LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'End'");
146  return;
147  }
148 
149  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
150  m_render->logDebugMarker("PAINTER_FRAME_END");
151  if (m_target == nullptr && m_swapControl)
152  {
153  m_render->Flush();
155  }
157 
158  m_mappedTextures.clear();
160 }
161 
163 {
164  if (!m_render)
165  return nullptr;
166 
167  if (m_imageToTextureMap.contains(Image))
168  {
169  if (!Image->IsChanged())
170  {
171  m_ImageExpireList.remove(Image);
172  m_ImageExpireList.push_back(Image);
173  return m_imageToTextureMap[Image];
174  }
175  DeleteFormatImagePriv(Image);
176  }
177 
178  Image->SetChanged(false);
179 
180  MythGLTexture *texture = nullptr;
181  for (;;)
182  {
183  texture = m_render->CreateTextureFromQImage(Image);
184  if (texture)
185  break;
186 
187  // This can happen if the cached textures are too big for GPU memory
188  if (m_hardwareCacheSize <= 8 * 1024 * 1024)
189  {
190  LOG(VB_GENERAL, LOG_ERR, "Failed to create OpenGL texture.");
191  return nullptr;
192  }
193 
194  // Shrink the cache size
196  LOG(VB_GENERAL, LOG_NOTICE, QString(
197  "Shrinking UIPainterMaxCacheHW to %1KB")
198  .arg(m_maxHardwareCacheSize / 1024));
199 
201  {
202  MythImage *expiredIm = m_ImageExpireList.front();
203  m_ImageExpireList.pop_front();
204  DeleteFormatImagePriv(expiredIm);
205  DeleteTextures();
206  }
207  }
208 
209  CheckFormatImage(Image);
211  m_imageToTextureMap[Image] = texture;
212  m_ImageExpireList.push_back(Image);
213 
215  {
216  MythImage *expiredIm = m_ImageExpireList.front();
217  m_ImageExpireList.pop_front();
218  DeleteFormatImagePriv(expiredIm);
219  DeleteTextures();
220  }
221 
222  return texture;
223 }
224 
225 void MythOpenGLPainter::DrawImage(const QRect &Dest, MythImage *Image,
226  const QRect &Source, int Alpha)
227 {
228  if (m_render)
229  {
230  // Drawing an image multiple times with the same VBO will stall most GPUs as
231  // the VBO is re-mapped whilst still in use. Use a pooled VBO instead.
232  MythGLTexture *texture = GetTextureFromCache(Image);
233  if (texture && m_mappedTextures.contains(texture))
234  {
235  QOpenGLBuffer *vbo = texture->m_vbo;
237  texture->m_destination = QRect();
238  m_render->DrawBitmap(texture, m_target, Source, Dest, nullptr, Alpha);
239  texture->m_destination = QRect();
240  texture->m_vbo = vbo;
243  }
244  else
245  {
246  m_render->DrawBitmap(texture, m_target, Source, Dest, nullptr, Alpha);
247  m_mappedTextures.append(texture);
248  }
249  }
250 }
251 
252 void MythOpenGLPainter::DrawRect(const QRect &Area, const QBrush &FillBrush,
253  const QPen &LinePen, int Alpha)
254 {
255  if ((FillBrush.style() == Qt::SolidPattern ||
256  FillBrush.style() == Qt::NoBrush) && m_render)
257  {
258  m_render->DrawRect(m_target, Area, FillBrush, LinePen, Alpha);
259  return;
260  }
261  MythPainter::DrawRect(Area, FillBrush, LinePen, Alpha);
262 }
263 
264 void MythOpenGLPainter::DrawRoundRect(const QRect &Area, int CornerRadius,
265  const QBrush &FillBrush,
266  const QPen &LinePen, int Alpha)
267 {
268  if ((FillBrush.style() == Qt::SolidPattern ||
269  FillBrush.style() == Qt::NoBrush) && m_render)
270  {
271  m_render->DrawRoundRect(m_target, Area, CornerRadius, FillBrush,
272  LinePen, Alpha);
273  return;
274  }
275  MythPainter::DrawRoundRect(Area, CornerRadius, FillBrush, LinePen, Alpha);
276 }
277 
279 {
280  if (m_imageToTextureMap.contains(Image))
281  {
282  QMutexLocker locker(&m_textureDeleteLock);
283  m_textureDeleteList.push_back(m_imageToTextureMap[Image]);
284  m_imageToTextureMap.remove(Image);
285  m_ImageExpireList.remove(Image);
286  }
287 }
288 
289 void MythOpenGLPainter::PushTransformation(const UIEffects &Fx, QPointF Center)
290 {
291  if (m_render)
292  m_render->PushTransformation(Fx, Center);
293 }
294 
296 {
297  if (m_render)
299 }
int m_maxHardwareCacheSize
Definition: mythpainter.h:130
void FreeResources(void) override
virtual void Teardown(void)
Definition: mythpainter.cpp:28
void DrawRoundRect(QOpenGLFramebufferObject *Target, const QRect &Area, int CornerRadius, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
QMap< MythImage *, MythGLTexture * > m_imageToTextureMap
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
void SetBackground(int Red, int Green, int Blue, int Alpha)
void SetViewPort(const QRect &Rect, bool ViewportOnly=false)
static int GetTextureDataSize(MythGLTexture *Texture)
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
static const GLuint kVertexSize
int m_hardwareCacheSize
Definition: mythpainter.h:129
std::list< MythGLTexture * > m_textureDeleteList
void logDebugMarker(const QString &Message)
virtual void DrawRoundRect(const QRect &area, int cornerRadius, const QBrush &fillBrush, const QPen &linePen, int alpha)
void DeleteFormatImagePriv(MythImage *Image) override
void DrawImage(const QRect &Dest, MythImage *Image, const QRect &Source, int Alpha) override
virtual void End()
Definition: mythpainter.h:51
MythGLTexture * GetTextureFromCache(MythImage *Image)
std::list< MythImage * > m_ImageExpireList
virtual void FreeResources(void)
Definition: mythpainter.h:49
void PopTransformation(void) override
MythGLTexture * CreateTextureFromQImage(QImage *Image)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:14
void PushTransformation(const UIEffects &Fx, QPointF Center=QPointF()) override
QOpenGLBuffer * m_vbo
virtual void DrawRect(const QRect &area, const QBrush &fillBrush, const QPen &linePen, int alpha)
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)
void DrawRect(const QRect &Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha) override
QOpenGLFramebufferObject * m_target
void PopTransformation(void)
QOpenGLBuffer * m_mappedBufferPool[MAX_BUFFER_POOL]
void DeleteTexture(MythGLTexture *Texture)
~MythOpenGLPainter() override
MythRenderOpenGL * m_render
void CheckFormatImage(MythImage *im)
bool IsChanged() const
Definition: mythimage.h:51
void PushTransformation(const UIEffects &Fx, QPointF &Center)
MythOpenGLPainter(MythRenderOpenGL *Render=nullptr, QWidget *Parent=nullptr)
void DrawRoundRect(const QRect &Area, int CornerRadius, const QBrush &FillBrush, const QPen &LinePen, int Alpha) override
#define MAX_BUFFER_POOL
virtual void Begin(QPaintDevice *parent)
Definition: mythpainter.h:50
void Begin(QPaintDevice *Parent) override
void ClearFramebuffer(void)
void SetMaximumCacheSizes(int hardware, int software)
QVector< MythGLTexture * > m_mappedTextures
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23