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