MythTV master
mythpainteropengl.cpp
Go to the documentation of this file.
1// C++
2#include <algorithm>
3
4// Qt
5#include <QtGlobal>
6#include <QCoreApplication>
7#include <QPainter>
8
9// MythTV
11#include "mythmainwindow.h"
12#include "mythrenderopengl.h"
13#include "mythpainteropengl.h"
14
16 MythMainWindow* Parent)
17 : MythPainterGPU(Parent)
18 , m_render(Render)
19{
21
22 if (!m_render)
23 LOG(VB_GENERAL, LOG_ERR, "OpenGL painter has no render device");
24}
25
27{
28 if (!m_render)
29 return;
30 if (!m_render->IsReady())
31 return;
32 OpenGLLocker locker(m_render);
33 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
34 m_render->logDebugMarker("PAINTER_RELEASE_START");
35 Teardown();
37 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
38 m_render->logDebugMarker("PAINTER_RELEASE_END");
39}
40
42{
43 OpenGLLocker locker(m_render);
44 ClearCache();
47 {
48 for (auto & buf : m_mappedBufferPool)
49 {
50 delete buf;
51 buf = nullptr;
52 }
54 }
55 for (auto * proc : std::as_const(m_procedurals))
56 delete proc;
57 m_procedurals.clear();
59}
60
62{
63 if (!m_render || m_textureDeleteList.empty())
64 return;
65
66 QMutexLocker gllocker(&m_imageAndTextureLock);
67 OpenGLLocker locker(m_render);
68 while (!m_textureDeleteList.empty())
69 {
70 MythGLTexture *texture = m_textureDeleteList.front();
72 m_render->DeleteTexture(texture);
73 m_textureDeleteList.pop_front();
74 }
75}
76
78{
79 LOG(VB_GENERAL, LOG_INFO, "Clearing OpenGL painter cache.");
80
81 QMutexLocker locker(&m_imageAndTextureLock);
82 QMapIterator<MythImage *, MythGLTexture*> it(m_imageToTextureMap);
83 while (it.hasNext())
84 {
85 it.next();
86 m_textureDeleteList.push_back(m_imageToTextureMap[it.key()]);
87 m_imageExpireList.remove(it.key());
88 }
89 m_imageToTextureMap.clear();
90}
91
92void MythOpenGLPainter::Begin(QPaintDevice *Parent)
93{
95
96 m_frameTime = QTime::currentTime().msecsSinceStartOfDay();
97 if (!(m_render && m_parent))
98 {
99 LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'Begin'");
100 return;
101 }
102
104 {
106 // initialise the VBO pool
107 std::generate(m_mappedBufferPool.begin(), m_mappedBufferPool.end(),
108 [&]() { return m_render->CreateVBO(static_cast<int>(MythRenderOpenGL::kVertexSize)); });
109 }
110
111 QSize currentsize = m_parent->size();
112
113 // check if we need to adjust cache sizes
114 // NOTE - don't use the scaled size if using high DPI. Our images are at the lower
115 // resolution
116 if (m_lastSize != currentsize)
117 {
118 // This will scale the cache depending on the resolution in use
119 static const int s_onehd = 1920 * 1080;
120 static const int s_basesize = 64;
121 m_lastSize = currentsize;
122 float hdscreens = (static_cast<float>(m_lastSize.width() + 1) * m_lastSize.height()) / s_onehd;
123 int cpu = std::max(static_cast<int>(hdscreens * s_basesize), s_basesize);
124 int gpu = cpu * 3 / 2;
125 SetMaximumCacheSizes(gpu, cpu);
126 }
127
128 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
129 m_render->logDebugMarker("PAINTER_FRAME_START");
130
133
134 // If master (have complete swap control) then bind default framebuffer and clear
135 if (m_viewControl.testFlag(Framebuffer))
136 {
137 m_render->BindFramebuffer(nullptr);
138 m_render->SetBackground(0, 0, 0, 255);
140 }
141
142 // If we have viewport control, set as needed.
143 if (m_viewControl.testFlag(Viewport))
144 {
145 // If using high DPI then scale the viewport
146 if (m_usingHighDPI)
147 currentsize *= m_pixelRatio;
148 m_render->SetViewPort(QRect(0, 0, currentsize.width(), currentsize.height()));
149 }
150}
151
153{
154 if (!m_render)
155 {
156 LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'End'");
157 return;
158 }
159
160 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
161 m_render->logDebugMarker("PAINTER_FRAME_END");
162
163 if (m_viewControl.testFlag(Framebuffer))
164 {
165 m_render->Flush();
167 }
169
170 m_mappedTextures.clear();
172}
173
175{
176 if (!m_render)
177 return nullptr;
178
179 QMutexLocker locker(&m_imageAndTextureLock);
180 if (m_imageToTextureMap.contains(Image))
181 {
182 if (!Image->IsChanged())
183 {
184 m_imageExpireList.remove(Image);
185 m_imageExpireList.push_back(Image);
186 return m_imageToTextureMap[Image];
187 }
189 }
190 locker.unlock();
191
192 Image->SetChanged(false);
193
194 int count = 0;
195 MythGLTexture* texture = nullptr;
196 while (texture == nullptr)
197 {
198 texture = m_render->CreateTextureFromQImage(Image);
199 if (texture != nullptr)
200 break;
201
202 // This can happen if the cached textures are too big for GPU memory
203 if ((count++ > 1000) || (m_hardwareCacheSize <= 8 * 1024 * 1024))
204 {
205 LOG(VB_GENERAL, LOG_ERR, "Failed to create OpenGL texture.");
206 return nullptr;
207 }
208
209 // Shrink the cache size
211 LOG(VB_GENERAL, LOG_NOTICE, QString("Shrinking UIPainterMaxCacheHW to %1KB")
212 .arg(m_maxHardwareCacheSize / 1024));
213
214 locker.relock();
216 {
217 MythImage *expiredIm = m_imageExpireList.front();
218 m_imageExpireList.pop_front();
219 DeleteFormatImagePriv(expiredIm);
221 }
222 locker.unlock();
223 }
224
225 CheckFormatImage(Image);
227 locker.relock();
228 m_imageToTextureMap[Image] = texture;
229 m_imageExpireList.push_back(Image);
230
232 {
233 MythImage *expiredIm = m_imageExpireList.front();
234 m_imageExpireList.pop_front();
235 DeleteFormatImagePriv(expiredIm);
237 }
238
239 return texture;
240}
241
242void MythOpenGLPainter::DrawImage(const QRect Dest, MythImage *Image,
243 const QRect Source, int Alpha)
244{
245 if (m_render)
246 {
247 qreal pixelratio = 1.0;
248 if (m_usingHighDPI && m_viewControl.testFlag(Viewport))
249 pixelratio = m_pixelRatio;
250
251 QRect dest = QRect(static_cast<int>(Dest.left() * pixelratio),
252 static_cast<int>(Dest.top() * pixelratio),
253 static_cast<int>(Dest.width() * pixelratio),
254 static_cast<int>(Dest.height() * pixelratio));
255
256 // Drawing an image multiple times with the same VBO will
257 // stall most GPUs as the VBO is re-mapped whilst still in
258 // use. Use a pooled VBO instead.
259 MythGLTexture *texture = GetTextureFromCache(Image);
260 if (texture && m_mappedTextures.contains(texture))
261 {
262 QOpenGLBuffer *vbo = texture->m_vbo;
264 texture->m_destination = QRect();
265 m_render->DrawBitmap(texture, nullptr, Source, dest,
266 nullptr, Alpha, pixelratio);
267 texture->m_destination = QRect();
268 texture->m_vbo = vbo;
271 }
272 else
273 {
274 m_render->DrawBitmap(texture, nullptr, Source, dest,
275 nullptr, Alpha, pixelratio);
276 m_mappedTextures.append(texture);
277 }
278 }
279}
280
281void MythOpenGLPainter::DrawProcedural(QRect Dest, int Alpha, const ProcSource& VertexSource, const ProcSource& FragmentSource, const QString &SourceHash)
282{
283 if (auto * shader = GetProceduralShader(VertexSource, FragmentSource, SourceHash); shader && m_render)
284 m_render->DrawProcedural(Dest, Alpha, nullptr, shader, m_frameTime);
285}
286
287QOpenGLShaderProgram* MythOpenGLPainter::GetProceduralShader(const ProcSource& VertexSource, const ProcSource& FragmentSource, const QString& SourceHash)
288{
289 if (!m_render)
290 return nullptr;
291
292 if (auto program = m_procedurals.find(SourceHash); program != m_procedurals.end())
293 return *program;
294
295 auto * result = m_render->CreateShaderProgram(QString(*VertexSource), QString(*FragmentSource));
296 m_procedurals.insert(SourceHash, result);
297 LOG(VB_GENERAL, LOG_INFO, QString("%1 procedural shaders cached").arg(m_procedurals.size()));
298 return result;
299}
300
310void MythOpenGLPainter::DrawRect(const QRect Area, const QBrush &FillBrush,
311 const QPen &LinePen, int Alpha)
312{
313 if ((FillBrush.style() == Qt::SolidPattern ||
314 FillBrush.style() == Qt::NoBrush) && m_render && !m_usingHighDPI)
315 {
316 m_render->DrawRect(nullptr, Area, FillBrush, LinePen, Alpha);
317 return;
318 }
319 MythPainterGPU::DrawRect(Area, FillBrush, LinePen, Alpha);
320}
321
322void MythOpenGLPainter::DrawRoundRect(const QRect Area, int CornerRadius,
323 const QBrush &FillBrush,
324 const QPen &LinePen, int Alpha)
325{
326 if ((FillBrush.style() == Qt::SolidPattern ||
327 FillBrush.style() == Qt::NoBrush) && m_render && !m_usingHighDPI)
328 {
329 m_render->DrawRoundRect(nullptr, Area, CornerRadius, FillBrush,
330 LinePen, Alpha);
331 return;
332 }
333 MythPainterGPU::DrawRoundRect(Area, CornerRadius, FillBrush, LinePen, Alpha);
334}
335
337{
338 QMutexLocker locker(&m_imageAndTextureLock);
339 if (m_imageToTextureMap.contains(Image))
340 {
342 m_imageToTextureMap.remove(Image);
343 m_imageExpireList.remove(Image);
344 }
345}
346
348{
349 if (m_render)
350 m_render->PushTransformation(Fx, Center);
351}
352
354{
355 if (m_render)
357}
QOpenGLBuffer * m_vbo
bool IsChanged() const
Definition: mythimage.h:51
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
MythRenderOpenGL * m_render
std::list< MythGLTexture * > m_textureDeleteList
MythGLTexture * GetTextureFromCache(MythImage *Image)
void DrawRect(QRect Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha) override
Draw a rectangle.
~MythOpenGLPainter() override
void Begin(QPaintDevice *Parent) override
void DrawProcedural(QRect Dest, int Alpha, const ProcSource &VertexSource, const ProcSource &FragmentSource, const QString &SourceHash) override
QVector< MythGLTexture * > m_mappedTextures
QHash< QString, QOpenGLShaderProgram * > m_procedurals
std::array< QOpenGLBuffer *, MAX_BUFFER_POOL > m_mappedBufferPool
void PopTransformation(void) override
void FreeResources(void) override
void DrawRoundRect(QRect Area, int CornerRadius, const QBrush &FillBrush, const QPen &LinePen, int Alpha) override
void PushTransformation(const UIEffects &Fx, QPointF Center=QPointF()) override
void DeleteFormatImagePriv(MythImage *Image) override
QOpenGLShaderProgram * GetProceduralShader(const ProcSource &VertexSource, const ProcSource &FragmentSource, const QString &SourceHash)
MythOpenGLPainter(MythRenderOpenGL *Render, MythMainWindow *Parent)
QRecursiveMutex m_imageAndTextureLock
std::list< MythImage * > m_imageExpireList
void DrawImage(QRect Dest, MythImage *Image, QRect Source, int Alpha) override
QMap< MythImage *, MythGLTexture * > m_imageToTextureMap
MythMainWindow * m_parent
ViewControls m_viewControl
virtual void DrawRect(QRect area, const QBrush &fillBrush, const QPen &linePen, int alpha)
float m_frameTime
Definition: mythpainter.h:134
int m_hardwareCacheSize
Definition: mythpainter.h:136
void CheckFormatImage(MythImage *im)
void SetMaximumCacheSizes(int hardware, int software)
virtual void Begin(QPaintDevice *)
Definition: mythpainter.h:54
virtual void DrawRoundRect(QRect area, int cornerRadius, const QBrush &fillBrush, const QPen &linePen, int alpha)
virtual void End()
Definition: mythpainter.h:55
int m_maxHardwareCacheSize
Definition: mythpainter.h:137
virtual void Teardown(void)
Definition: mythpainter.cpp:27
virtual void FreeResources(void)
Definition: mythpainter.h:53
MythGLTexture * CreateTextureFromQImage(QImage *Image)
void DrawProcedural(QRect Area, int Alpha, QOpenGLFramebufferObject *Target, QOpenGLShaderProgram *Program, float TimeVal)
void DrawRoundRect(QOpenGLFramebufferObject *Target, QRect Area, int CornerRadius, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
void DrawBitmap(MythGLTexture *Texture, QOpenGLFramebufferObject *Target, QRect Source, QRect Destination, QOpenGLShaderProgram *Program, int Alpha=255, qreal Scale=1.0)
void ClearFramebuffer(void)
void SetViewPort(QRect Rect, bool ViewportOnly=false) override
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
void PushTransformation(const UIEffects &Fx, QPointF &Center)
void PopTransformation(void)
void logDebugMarker(const QString &Message)
void SetBackground(uint8_t Red, uint8_t Green, uint8_t Blue, uint8_t Alpha)
QOpenGLShaderProgram * CreateShaderProgram(const QString &Vertex, const QString &Fragment)
void DeleteTexture(MythGLTexture *Texture)
void DrawRect(QOpenGLFramebufferObject *Target, QRect Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
static int GetTextureDataSize(MythGLTexture *Texture)
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
std::shared_ptr< QByteArray > ProcSource
Definition: mythpainter.h:32
static constexpr size_t MAX_BUFFER_POOL