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 for (auto it = m_imageToTextureMap.cbegin();
83 it != m_imageToTextureMap.cend(); ++it)
84 {
85 m_textureDeleteList.push_back(m_imageToTextureMap[it.key()]);
86 m_imageExpireList.remove(it.key());
87 }
88 m_imageToTextureMap.clear();
89}
90
91void MythOpenGLPainter::Begin(QPaintDevice *Parent)
92{
94
95 m_frameTime = QTime::currentTime().msecsSinceStartOfDay();
96 if (!(m_render && m_parent))
97 {
98 LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'Begin'");
99 return;
100 }
101
103 {
105 // initialise the VBO pool
106 std::generate(m_mappedBufferPool.begin(), m_mappedBufferPool.end(),
107 [&]() { return m_render->CreateVBO(static_cast<int>(MythRenderOpenGL::kVertexSize)); });
108 }
109
110 QSize currentsize = m_parent->size();
111
112 // check if we need to adjust cache sizes
113 // NOTE - don't use the scaled size if using high DPI. Our images are at the lower
114 // resolution
115 if (m_lastSize != currentsize)
116 {
117 // This will scale the cache depending on the resolution in use
118 static const int s_onehd = 1920 * 1080;
119 static const int s_basesize = 64;
120 m_lastSize = currentsize;
121 float hdscreens = (static_cast<float>(m_lastSize.width() + 1) * m_lastSize.height()) / s_onehd;
122 int cpu = std::max(static_cast<int>(hdscreens * s_basesize), s_basesize);
123 int gpu = cpu * 3 / 2;
124 SetMaximumCacheSizes(gpu, cpu);
125 }
126
127 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
128 m_render->logDebugMarker("PAINTER_FRAME_START");
129
132
133 // If master (have complete swap control) then bind default framebuffer and clear
134 if (m_viewControl.testFlag(Framebuffer))
135 {
136 m_render->BindFramebuffer(nullptr);
137 m_render->SetBackground(0, 0, 0, 255);
139 }
140
141 // If we have viewport control, set as needed.
142 if (m_viewControl.testFlag(Viewport))
143 {
144 // If using high DPI then scale the viewport
145 if (m_usingHighDPI)
146 currentsize *= m_pixelRatio;
147 m_render->SetViewPort(QRect(0, 0, currentsize.width(), currentsize.height()));
148 }
149}
150
152{
153 if (!m_render)
154 {
155 LOG(VB_GENERAL, LOG_ERR, "FATAL ERROR: No render device in 'End'");
156 return;
157 }
158
159 if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
160 m_render->logDebugMarker("PAINTER_FRAME_END");
161
162 if (m_viewControl.testFlag(Framebuffer))
163 {
164 m_render->Flush();
166 }
168
169 m_mappedTextures.clear();
171}
172
174{
175 if (!m_render)
176 return nullptr;
177
178 QMutexLocker locker(&m_imageAndTextureLock);
179 if (m_imageToTextureMap.contains(Image))
180 {
181 if (!Image->IsChanged())
182 {
183 m_imageExpireList.remove(Image);
184 m_imageExpireList.push_back(Image);
185 return m_imageToTextureMap[Image];
186 }
188 }
189 locker.unlock();
190
191 Image->SetChanged(false);
192
193 int count = 0;
194 MythGLTexture* texture = nullptr;
195 while (texture == nullptr)
196 {
197 texture = m_render->CreateTextureFromQImage(Image);
198 if (texture != nullptr)
199 break;
200
201 // This can happen if the cached textures are too big for GPU memory
202 if ((count++ > 1000) || (m_hardwareCacheSize <= 8 * 1024 * 1024))
203 {
204 LOG(VB_GENERAL, LOG_ERR, "Failed to create OpenGL texture.");
205 return nullptr;
206 }
207
208 // Shrink the cache size
210 LOG(VB_GENERAL, LOG_NOTICE, QString("Shrinking UIPainterMaxCacheHW to %1KB")
211 .arg(m_maxHardwareCacheSize / 1024));
212
213 locker.relock();
215 {
216 MythImage *expiredIm = m_imageExpireList.front();
217 m_imageExpireList.pop_front();
218 DeleteFormatImagePriv(expiredIm);
220 }
221 locker.unlock();
222 }
223
224 CheckFormatImage(Image);
226 locker.relock();
227 m_imageToTextureMap[Image] = texture;
228 m_imageExpireList.push_back(Image);
229
231 {
232 MythImage *expiredIm = m_imageExpireList.front();
233 m_imageExpireList.pop_front();
234 DeleteFormatImagePriv(expiredIm);
236 }
237
238 return texture;
239}
240
241void MythOpenGLPainter::DrawImage(const QRect Dest, MythImage *Image,
242 const QRect Source, int Alpha)
243{
244 if (m_render)
245 {
246 qreal pixelratio = 1.0;
247 if (m_usingHighDPI && m_viewControl.testFlag(Viewport))
248 pixelratio = m_pixelRatio;
249
250 QRect dest = QRect(static_cast<int>(Dest.left() * pixelratio),
251 static_cast<int>(Dest.top() * pixelratio),
252 static_cast<int>(Dest.width() * pixelratio),
253 static_cast<int>(Dest.height() * pixelratio));
254
255 // Drawing an image multiple times with the same VBO will
256 // stall most GPUs as the VBO is re-mapped whilst still in
257 // use. Use a pooled VBO instead.
258 MythGLTexture *texture = GetTextureFromCache(Image);
259 if (texture && m_mappedTextures.contains(texture))
260 {
261 QOpenGLBuffer *vbo = texture->m_vbo;
263 texture->m_destination = QRect();
264 m_render->DrawBitmap(texture, nullptr, Source, dest,
265 nullptr, Alpha, pixelratio);
266 texture->m_destination = QRect();
267 texture->m_vbo = vbo;
270 }
271 else
272 {
273 m_render->DrawBitmap(texture, nullptr, Source, dest,
274 nullptr, Alpha, pixelratio);
275 m_mappedTextures.append(texture);
276 }
277 }
278}
279
280void MythOpenGLPainter::DrawProcedural(QRect Dest, int Alpha, const ProcSource& VertexSource, const ProcSource& FragmentSource, const QString &SourceHash)
281{
282 if (auto * shader = GetProceduralShader(VertexSource, FragmentSource, SourceHash); shader && m_render)
283 m_render->DrawProcedural(Dest, Alpha, nullptr, shader, m_frameTime);
284}
285
286QOpenGLShaderProgram* MythOpenGLPainter::GetProceduralShader(const ProcSource& VertexSource, const ProcSource& FragmentSource, const QString& SourceHash)
287{
288 if (!m_render)
289 return nullptr;
290
291 if (auto program = m_procedurals.find(SourceHash); program != m_procedurals.end())
292 return *program;
293
294 auto * result = m_render->CreateShaderProgram(QString(*VertexSource), QString(*FragmentSource));
295 m_procedurals.insert(SourceHash, result);
296 LOG(VB_GENERAL, LOG_INFO, QString("%1 procedural shaders cached").arg(m_procedurals.size()));
297 return result;
298}
299
309void MythOpenGLPainter::DrawRect(const QRect Area, const QBrush &FillBrush,
310 const QPen &LinePen, int Alpha)
311{
312 if ((FillBrush.style() == Qt::SolidPattern ||
313 FillBrush.style() == Qt::NoBrush) && m_render && !m_usingHighDPI)
314 {
315 m_render->DrawRect(nullptr, Area, FillBrush, LinePen, Alpha);
316 return;
317 }
318 MythPainterGPU::DrawRect(Area, FillBrush, LinePen, Alpha);
319}
320
321void MythOpenGLPainter::DrawRoundRect(const QRect Area, int CornerRadius,
322 const QBrush &FillBrush,
323 const QPen &LinePen, int Alpha)
324{
325 if ((FillBrush.style() == Qt::SolidPattern ||
326 FillBrush.style() == Qt::NoBrush) && m_render && !m_usingHighDPI)
327 {
328 m_render->DrawRoundRect(nullptr, Area, CornerRadius, FillBrush,
329 LinePen, Alpha);
330 return;
331 }
332 MythPainterGPU::DrawRoundRect(Area, CornerRadius, FillBrush, LinePen, Alpha);
333}
334
336{
337 QMutexLocker locker(&m_imageAndTextureLock);
338 if (m_imageToTextureMap.contains(Image))
339 {
341 m_imageToTextureMap.remove(Image);
342 m_imageExpireList.remove(Image);
343 }
344}
345
347{
348 if (m_render)
349 m_render->PushTransformation(Fx, Center);
350}
351
353{
354 if (m_render)
356}
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:131
int m_hardwareCacheSize
Definition: mythpainter.h:133
void CheckFormatImage(MythImage *im)
void SetMaximumCacheSizes(int hardware, int software)
virtual void Begin(QPaintDevice *)
Definition: mythpainter.h:51
virtual void DrawRoundRect(QRect area, int cornerRadius, const QBrush &fillBrush, const QPen &linePen, int alpha)
virtual void End()
Definition: mythpainter.h:52
int m_maxHardwareCacheSize
Definition: mythpainter.h:134
virtual void Teardown(void)
Definition: mythpainter.cpp:27
virtual void FreeResources(void)
Definition: mythpainter.h:50
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:29
static constexpr size_t MAX_BUFFER_POOL